Published: Dec 26, 2019
Now that Microsoft has released the set of REST-based Exchange Online cmdlets in the Exchange Online Management module, people are experimenting with the new cmdlets to discover what needs to be done to upgrade scripts.
When reviewing Chapter 13 for the December 2019 update of the Office 365 for IT Pros eBook to add information about PowerShell support for private channels in Teams, I also took the chance to update one of the examples showing how to use Exchange Online cmdlets with Office 365 Groups. Some interesting gotchas were encountered. Before getting into the details of what those gotchas were, let’s think about how quickly we can fetch a set of group objects to process.
When you connect a PowerShell session to Exchange Online, the set of cmdlets designed to manage Office 365 like Get-UnifiedGroup, Get-UnifiedGroupLinks, and so on become available for use. The Get-UnifiedGroup cmdlet fetches details about Office 365 Groups and is used extensively across in scripts published in blogs, GitHub, or the TechNet Gallery. Although Get-UnifiedGroup is effective at what it does, it’s slow. Very slow. And that slowness gets worse as the number of groups grows.
For this reason, my advice has long been to avoid using Get-UnifiedGroup to create a set of group mailboxes for processing and to use the Get-Recipient cmdlet instead. Get-Recipient is much faster because it doesn’t have to do as much processing to fetch the properties of each group object. To create a set of groups for processing, you could run a command like:
$Groups = Get-UnifiedGroup -ResultSize Unlimited
Running Get-Recipient to fetch the set of group mailboxes is much faster:
$Groups = Get-Recipient -RecipientTypeDetails GroupMailbox -ResultSize Unlimited
Try it and see just how quick Get-Recipient is.
In any case, like all the new REST cmdlets, the Get-ExoRecipient cmdlet is faster than its Remote PowerShell equivalent, especially when you need to process large quantities of objects. The Get-ExoRecipient cmdlet is the fastest way to create a list of Office 365 Groups and should be used for this purpose whenever possible.
Which brings us to an example of how to convert some code from the Remote PowerShell cmdlets to REST. This example uses Get-Recipient to find Office 365 Groups and pipes the objects through Get-MailboxFolderStatistics to report the number of items in the Inbox folder of the group mailbox. We also report the date of the newest item created in the Inbox. Calculated properties are used to create more readable output.
Here’s the original code:
Get-Recipient -RecipientTypeDetails GroupMailbox -ResultSize Unlimited | Sort DisplayName | Get-MailboxFolderStatistics -FolderScope Inbox -IncludeOldestAndNewestItems | Format-Table @{"Name"="Group";"Expression"={$_.Identity.Split("\")[0]}}, ItemsinFolder, @{"Name"="Size"; "Expression"={$_.FolderSize.Split("(")[0]}}, @{"Name"="Newest Item"; "Expression"={Get-Date($_.NewestItemReceivedDate) -format g}}
The output is something like this:
Group ItemsInFolder Size Newest Item ----- ------------- ---- ----------- 2021Edition_c832eaa2-f5b8-4f2e-a745-0595074a5 52 196.92 KB 12 Mar 2019 14:43 academydocuments_b3192809-1a22-4d31-8538-80e7350de 447 45.12 MB 14 May 2018 11:33
The group name looks and is strange. It’s the identity returned by Get-MailboxFolderStatistics less the name of the folder (Inbox). The identity comes from the name given to the group object when it is created.
Looking for items in a folder is a convenient way to check the activity of Outlook-based Office 365 Groups which use email for conversations. If new items aren’t be posted into the Inbox, the group isn’t active. The same technique can be used to examine items in the Team Chat folder to discover the last time someone posted a message to a channel conversation in a team-enabled group.
The converted code uses Get-ExoRecipient to fetch the set of groups and then pipes each mailbox to Get-ExoMailboxFolderStatistics to return the number of items and the date of the latest item in the Inbox folder. Here’s the updated code:
Get-ExoRecipient -RecipientTypeDetails GroupMailbox -ResultSize Unlimited | Select-Object -Property @{Name = 'UserPrincipalName'; Expression = {$_.PrimarySmtpAddress}} | Get-ExoMailboxFolderStatistics -FolderScope Inbox -IncludeOldestAndNewestItems | Format-Table @{"Name"="Group";"Expression"={$_.Identity.Split("\")[0]}}, ItemsinFolder, @{"Name"="Size"; "Expression"={$_.FolderSize.Value.ToString('a')}}, @{"Name"="Newest Item"; "Expression"={ReturnEuroDate($_.NewestItemReceivedDate)}}
Even allowing for the replacement of Remote PowerShell cmdlets by REST cmdlets, the code is obviously different. Here’s why:
If ($MbxStats.TotalItemSize.Value -ne $NULL) {$MbxSizeMB = (($MbxStats.TotalItemSize.ToString().Split('(')[1].Split(')')[0].Replace(',','').Split(' ')[0])/1MB).ToUInt32($NULL)}
Reporting the folder size returned by Get-ExoMailboxFolderStatistics in megabytes is much simpler:
$MbxStats.FolderSize.Value.ToMB()
The same output is generated:
Group ItemsInFolder Size Newest Item ----- ------------- ---- ----------- [email protected] 52 196.92 KB 12 Mar 2019 14:33 [email protected] 447 45.12 MB 14 May 2018.11:33
Here’s the function to convert U.S. dates to a local format.
Function ReturnEuroDate([String]$InputDate) { # Input date is something like 10/12/2017 9:15 PM [String]$Return = $Null $SplitBits = $InputDate.Split(" ") # Convert from U.S. date $DateParts = $SplitBits[0] -split "/" $EuroDate = "$($dateparts[1])/$($dateParts[0])/$($dateParts[2])" [int]$Hours = $SplitBits[1].Split(":")[0]; [int]$Minutes = $SplitBits[1].Split(":")[1] If ($SplitBits[2] -eq "PM" -and $Hours -ne 12) {$Hours+= 12} #Add 12 to hours if PM $EuroDate = Get-Date($Eurodate + " " + $Hours + ":" + $Minutes) -format g $OutputDate = $EuroDate Return $OutputDate }
Converting code in old scripts to use the REST cmdlets seems straightforward on the surface and sometimes is the case. However, small and important differences await the unprepared, which means that you should set aside a reasonable amount of time to rewrite, test, and measure the performance of scripts converted to REST.
Although the REST cmdlets include auto-retry to make them less susceptible to transient errors, don’t forget to add some error handling into your code. Old routines handle the errors generated by Remote PowerShell cmdlets; the new cmdlets bring their own errors. While fewer, they still exist.