Since the introduction of PowerShell in the form of the Exchange Management Shell (EMS) in Exchange 2007, many scripts have been written to ease the burden of Exchange administration by automating common operations. Microsoft has broadened the scope and depth of EMS in every release by enabling management of more and more parts of the product through scripting and the current on-premises version (Exchange 2013 CU3 as I write this post) makes more than 950 cmdlets available.
Libraries of useful EMS scripts are available for free download from web sites (check out the TechNet gallery of Exchange scripts) and literally thousands of examples can be found to deal with almost every conceivable management challenge that you’ll encounter on an Exchange server.
Of course, EMS is very different when it comes to Exchange Online running in Office 365. One of the reasons why companies sign up for Office 365 is so that they never have to deal with the mundane details of day-to-day server administration. So when you connect to Office 365 with PowerShell and run the necessary commands to establish a remote Exchange administration session, far fewer cmdlets are made available. Depending on the Exchange plan you have signed up for (which dictates the functionality that you can use and therefore the underlying cmdlets that are exposed), you’ll can run 340 and 400 cmdlets. This shouldn’t come as a surprise as all the cmdlets that deal with servers (like Get-ExchangeServer) or high availability (Get-MailboxDatabaseCopyStatus) or transport (Get-Queue) lose their meaning when you use a hosted service and have no control over these objects.
The question then arises whether you can usefully take a PowerShell script written for on-premises Exchange and use it with Exchange Online. The answer is “it depends” and the dependencies include:
- The cmdlets used in the script. If they’re not supported by Exchange Online, you are out of luck. A variation on the theme is where a cmdlet has been replaced with a new cmdlet. In this case, you might decide to upgrade the script to use the new cmdlet so that it is not obsolete by changes made to the service.
- The environment that the code was written to run within. When you run a script inside EMS, you take advantage of the environment created by the work done by the EMS initialization script. For instance, credentials are established and you are connected to an Exchange server, so you can do work immediately. Neither of these conditions are true if you simply run PowerShell. You will have to connect to Office 365, provide credentials, and then make sure that the credentials are available should the need arise to run cmdlets later on in the session.
- The version of PowerShell. Exchange 2013 currently uses PowerShell 3.0 but Office 365 uses PowerShell 4.0. Does this matter? Well, it might, if the code is sensitive to version changes. The point is that you don’t have control over what version of anything Microsoft choses to run within Office 365, so prepare to be surprised.
With all that in mind, there’s still a huge amount of value to be extracted by adopting on-premises scripts to run against Exchange Online. To prove the point, let’s take a popular script called EASDeviceReport.ps1 published by MVP Paul Cunningham and examine what needs to be done to make it run against Exchange Online (you have to register with the ExchangeServerPro.com site before you can download the script; Paul assures me that he only uses the email addresses that he gathers for a particularly well-chosen form of spam).
You can download a script and run it against Exchange Online to see what happens. That’s possibly as good a way as any of finding out where problems lurk. The caveat is that you should always examine the code first to see if anything bad or potentially damaging is present as you never know what weirdness might have been inserted into a script. In any case, after an examination I ran the code and PowerShell output some problems.
Nothing too bad was revealed and some data was output. The script has a simple purpose: to report on the Exchange ActiveSync (EAS) devices that are connected to an Exchange organization. To do this, it runs the Get-CASMailbox cmdlet to identify mailboxes that have EAS partnerships. As you might recall, every time you connect a mobile device to Exchange with EAS, a partnership is created to identify the connection between device and mailbox. Managing those partnerships can be a challenge, as explained in this post.
Running Get-CASMailbox against a large Exchange organization will take a long time to complete. If you have more than a couple of thousand mailboxes in the organization (tenant domain), you might want to break up the processing by using a filter to select particular groups of users.
After Get-CASMailbox creates a collection of mailboxes that have EAS partnerships, the script then processes the set of mailboxes and uses the Get-ActiveSyncDeviceStatistics cmdlet to fetch information about each device partnered with each mailbox. In passing, let me note that this cmdlet is due to be deprecated in the future and has been replaced by Get-MobileDeviceStatistics in Exchange 2013 and later (including Exchange Online). For this reason it’s best to replace the cmdlet when running against Exchange Online.
Information is captured for each device and written into a CSV file. After all the devices are processed, the script either exits and you have the joy of reviewing data in a CSV file, or, you can specify the SendEmail parameter and have the script dispatch a message containing the data to you. Helpfully, the data is first converted to HTML, which makes it much easier to read.
Sending email from a PowerShell session connected to Office 365 is not hard – it’s just different. Mail stubbornly refused to flow when I tried it, so some further investigation was required.
Paul’s on-premises script uses the Send-MailMessage cmdlet to send its message as follows:
Send-MailMessage @smtpsettings -Body $htmlreport -BodyAsHtml -Attachments $reportfile
The first error was the reported lack of an SMTP server. We have to know whether Office 365 makes an SMTP available to clients and how to connect to it. Office 365 certainly supports both POP3 and IMAP4 clients, both of which need an SMTP server to send outbound messages, so the easiest way to find this information is to use Outlook Web App to connect to your mailbox, select Options, and then the account section, and then click the link to reveal POP3 and IMAP4 settings. I saw that my tenant domain uses smtp.office365.com, so I tried to use that and found that the attempted connection was rejected due to an authentication failure.
Looking at the settings again, they show that an SSL connection is required to port 587. Aha! The original code attempts to create a non-SSL connection. Also, it depends on the credentials of the user sending the message already being available within the PowerShell session. To make the code work with Office 365, I changed it to:
Send-MailMessage @smtpsettings -Body $htmlreport -BodyAsHtml -Attachments $reportfile -Credential $O365Cred -Port 587 –useSSL
You’ll notice that the code doesn’t specify the SMTP server. That’s because the script allows this to be passed as a parameter. The mail server name is then stored in a variable in the @smtpsettings reference above. I could also have passed it as a value to the –SmtpServer parameter and added it to the call to Send-MailMessage.
How were credentials passed to the script? You can see that the are specified to Send-MailMessage in the $O365Cred variable. This is declared as a global variable in the Connect-ExchangeOnline function in my PowerShell profile. Every time I run Connect-ExchangeOnline (the instructions to build the function are here), the Get-Credential cmdlet collects my credentials (Office 365 account name and password) and uses them to establish a PowerShell session with Exchange Online. The credentials are retained in the $O365Cred global variable and are therefore available to other scripts.
After making the changes to Send-MailMessage the script ran perfectly and I received a nicely formatted email message containing details of all of the ActiveSync partnerships known within my tenant domain.
Downloading the script and debugging it to make it run successfully on Office 365 took me no more than 30 minutes, including time to check details of various cmdlets and play around a bit. I am not an accomplished scripter by any means and a more practiced individual would do the job much quicker. Then again, another script might pose a much tougher challenge.
The point is this. Exchange on-premises and Exchange Online share a common heritage and code base. Code that this written for on-premises deployments can be useful when run against its cloud cousin. Libraries of EMS scripts are available to be downloaded free and played with to your heart’s content. What’s not to like about this situation?
Follow Tony @12Knocksinna