Table of Contents
Testing the Performance of the Exchange REST Cmdlets

Yesterday I delivered a rapid-fire 20-minute session at the Microsoft Ignite 2019 conference to report my experience of working with the new REST-based PowerShell cmdlets for Exchange Online. You can download the new cmdlets from the PowerShell gallery (the same module loads the older Remote PowerShell cmdlets and the REST-based cmdlets). You can listen to the recording online (voice and slides only, thankfully).
A more reflective article about using the cmdlets in scripts is available on Petri.com. This post has a link to a copy of the deck (below) and the code examples I used to test the new cmdlets. Hopefully the scripts are a useful starting point for your exploration of the new cmdlets.
Testing Scripts
I use the Measure-Command cmdlet to test the raw performance of the old (Remote PowerShell) and new (REST) cmdlets. Measure-Command takes a chunk of code and reports how long it took to execute the code. With a little bit of formatting, you get some nice data (Figure 1).

Here’s a basic example to run the Get-Mailbox cmdlet to fetch all user mailboxes ten times.
# Performance test for Get-Mailbox Write-Host "Running Remote PowerShell Test..." $TotalSeconds = 0; $TotalMbx = 0 For ($i=0; $i -lt 10 ) { $i++ Write-Host "Processing run" $i $RPSResult = Measure-Command { $MbxRPS = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox | Select DisplayName, ProhibitSendReceiveQuota, WhenCreated, WhenChanged } $MbxSec = [math]::round(($MbxRPS.Count/$RPSResult.TotalSeconds),2) $Result = "RPS took " + $RPSResult.TotalSeconds + " seconds (" + $MbxRPS.count + ") mailboxes: averaging " + $MbxSec + " mailboxes/second" Write-Host $Result $TotalSeconds = $TotalSeconds + $RPSResult.TotalSeconds $TotalMbx = $TotalMbx + $MbxRPS.Count } Write-Host "" Write-Host "Remote PowerShell Results" Write-Host "-------------------------" Write-Host "Total runs: " $i Write-Host "Total mailboxes: " $TotalMbx Write-Host "Total seconds: " $TotalSeconds Write-Host "Avg Mbx/Sec: " ([math]::round(($TotalMbx/$TotalSeconds),2))
Get-ExoMailbox is the equivalent REST cmdlet. Here’s what I used to test its performance:
# Performance test for Exchange REST cmdlet Get-ExoMailbox # REST cmdlets return DisplayName in minimum property set and ProhibitSendReceiveQuota in the Quota set Write-Host "Running RESTful Test..." $TotalSeconds = 0; $TotalMbx = 0 For ($i=0; $i -lt 10 ) { $i++ Write-Host "Processing run" $i $RESTResult = Measure-Command { $Mbx = Get-ExoMailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox -PropertySets Quota -Properties WhenCreated, WhenChanged } $MbxSec = [math]::round(($Mbx.Count/$RESTResult.TotalSeconds),2) $Result = "REST took " + $RESTResult.TotalSeconds + " seconds (" + $Mbx.count + ") mailboxes: averaging " + $MbxSec + " mailboxes/second" $TotalSeconds = $TotalSeconds + $RESTResult.TotalSeconds $TotalMbx = $TotalMbx + $Mbx.Count Write-Host $Result } Write-Host "" Write-Host "RESTful Cmdlets Results" Write-Host "-----------------------" Write-Host "Total runs: " $i Write-Host "Total mailboxes: " $TotalMbx Write-Host "Total seconds: " $TotalSeconds Write-Host "Avg Mbx/Sec: " ([math]::round(($TotalMbx/$TotalSeconds),2))
Testing Mailbox Statistics with the Exchange REST Cmdlet
Just fetching a bunch of mailboxes isn’t very exciting, even with a new cmdlet. The next test is to call Get-MailboxStatistics to return the number of items and the quota used for each mailbox. The example below shows how I tested using Get-ExoMailbox to fetch the mailboxes and Get-ExoMailboxStatistics to return the statistics. In the call to Get-ExoMailbox, you see that we request the return of the StatisticsSeed property set. The REST cmdlets don’t return all properties by default. You must tell them what properties you want to be returned for each object. This reduces the amount of processing needed for each object and makes the cmdlet more efficient.
Another big point to note is the use the GUID of the mailbox owner as the identity for Get-ExoMailboxStatistics. Unlike the older cmdlets, the REST cmdlets allow you only to pass a user principal name or GUID as the identity. Azure AD cmdlets use the same GUID, so I use it here.
# Use Exchange REST Cmdlet to test mailbox statistics $Iterations = Read-Host "Enter number of iterations" $Mailboxes = Read-Host "Enter number of mailboxes to test" If ($Mailboxes -gt 4999) {$Mailboxes = "Unlimited"} Write-Host "Running RESTful Test..." $TotalSeconds = 0; $TotalMbx = 0 For ($i=0; $i -lt $iterations ) { $i++ # Write-Host "Processing run" $i $RESTResult = Measure-Command {$Mbx = Get-ExoMailbox -RecipientTypeDetails UserMailbox -PropertySet StatisticsSeed -ResultSize $Mailboxes; ForEach ($M in $Mbx) { $X = Get-ExoMailboxStatistics -Identity $M.ExternalDirectoryObjectId ; Write-Host "Run" $i (Get-Date -format g) $X.DisplayName "has" $X.ItemCount "items with size" $X.TotalItemSize }} $MbxSec = [math]::round(($Mbx.Count/$RESTResult.TotalSeconds),2) $Result = "REST took " + $RESTResult.TotalSeconds + " seconds (" + $Mbx.count + ") mailboxes: averaging " + $MbxSec + " mailboxes/second" $TotalSeconds = $TotalSeconds + $RESTResult.TotalSeconds $TotalMbx = $TotalMbx + $Mbx.Count Write-Host $Result } Write-Host "" Write-Host "RESTful Cmdlets Results" Write-Host "-----------------------" Write-Host "Total runs: " $i Write-Host "Total mailboxes: " $TotalMbx Write-Host "Total seconds: " $TotalSeconds Write-Host "Avg Mbx/Sec: " ([math]::round(($TotalMbx/$TotalSeconds),2))
Piping is Best
The previous example uses a ForEach loop to process each mailbox. This works and it’s a technique that’s extensively used in scripts that process Exchange information. However, you can get even better performance if you pipe data from one REST cmdlet to another because multithreading then kicks in. Here’s an example.
# Optimized Pipeline Version for Exchange REST cmdlets test # RESTful Cmdlets Version [int]$Iterations = Read-Host "Enter number of iterations" [int]$Mailboxes = Read-Host "Enter number of mailboxes to test" If ($Mailboxes -gt 4999) {$Mailboxes = "Unlimited"} Write-Host "Running RESTful Test..." $TotalSeconds = 0; $TotalMbx = 0 For ($i=0; $i -lt $iterations; ) { $i++ Write-Host "Processing run" $i $RESTResult = Measure-Command { $Mbx = Get-ExoMailbox -RecipientTypeDetails UserMailbox -PropertySet StatisticsSeed -ResultSize $Mailboxes; # Write-Host "Run" $i (Get-Date -format g) $Mbx | Get-EXOMailboxStatistics | ForEach-Object { Write-Host "Run" $i (Get-Date -format g) $_.DisplayName "has" $_.ItemCount "items with size" $_.TotalItemSize } } $MbxSec = [math]::round(($Mbx.Count/$RESTResult.TotalSeconds),2) $Result = "REST took " + $RESTResult.TotalSeconds + " seconds (" + $Mbx.count + ") mailboxes: averaging " + $MbxSec + " mailboxes/second" $TotalSeconds = $TotalSeconds + $RESTResult.TotalSeconds $TotalMbx = $TotalMbx + $Mbx.Count Write-Host $Result } Write-Host "" Write-Host "RESTful Cmdlets Results" Write-Host "-----------------------" Write-Host "Total runs: " $i Write-Host "Total mailboxes: " $TotalMbx Write-Host "Total seconds: " $TotalSeconds Write-Host "Avg Mbx/Sec: " ([math]::round(($TotalMbx/$TotalSeconds),2))
Although piping is undoubtedly faster, in a practical sense it’s often not possible to put everything you might want to do to process mailbox data through the pipe, which is why many scripts use ForEach loops. However, if you can convert your code to a piped version, you will get much better performance, which is something to consider.
Looking for more information about writing PowerShell for Exchange Online and the other parts of Office 365? The Office 365 for IT Pros eBook includes tons of examples, all written with tender loving care.
3 Replies to “Testing the New Exchange Online REST-based PowerShell Cmdlets”