Error handling in Exchange Remote PowerShell sessions

​I’m hardly an expert of PowerShell, but this topic seems to be greatly overlooked by the community. Most people seem to agree that the preferred way of error handling in PowerShell is via the try-catch-finally statement. The method is easy to use and it allows you to capture specific error types and perform a different action depending on the exception type. Here’s a simple example:

PS C:\> Try { Get-process non-existant-process -ErrorAction Stop } catch { Write-Host "Oh noes! You made a boo boo." -ForegroundColor DarkGray}

Oh noes! You made a boo boo.

However, when it comes to using remote PowerShell with Exchange, things get ugly fast. Let’s run the above example with the Get-Mailbox cmdlet:

PS C:\> Try { Get-Mailbox non-existant-mailbox -ErrorAction Stop } catch { Write-Host "Oh noes! You made a boo boo." -ForegroundColor DarkGray}

The operation couldn't be performed because object 'non-existant-mailbox' couldn't be found on 'DBXPR03A001DC01.EURPR03A001.prod.outlook.com'.
+ CategoryInfo          : NotSpecified: (:) [Get-Mailbox], ManagementObjectNotFoundException
+ FullyQualifiedErrorId : [Server=DBXPR03MB617,RequestId=04c5f21e-b716-4a67-ac67-52f3c19af39c,TimeStamp=3/1/2015 2:52:47 PM] [FailureCategory=Cmdlet-ManagementObjectNotFoundException] B5614317,Microsoft.Exchange.Management.RecipientTasks.GetMailbox
+ PSComputerName        : outlook.office365.com

The result is much different this time! Not only the ‘standard’ error message is displayed, but our custom error handling code is completely ignored. There are several reasons behind this, number one being the fact that you are not actually running the cmdlet – you are instead running a ‘proxy’ function in a implicit remoting session. See for yourself, get the definition of the Get-Mailbox cmdlet:

Get-Command Get-Mailbox | fl

OK, so how does knowing that small fact help us? It doesn’t, not really. It offers a workaround for the simplest of cases, and just that. Instead of using the proxy function, you can use Invoke-Command to ‘wrap’ it:

PS C:\> $ses = Get-PSSession | ? {$_.ConfigurationName -eq 'Microsoft.Exchange' -and ($_.Runspace.ConnectionInfo.ConnectionUri.Host -eq "outlook.office365.com") -and $_.Availability -eq "Available"}

PS C:\> try { Invoke-Command -Session $ses -ScriptBlock { Get-Mailbox non-existant-mailbox } -ErrorAction Stop } catch {Write-Host "Oh noes! You made a boo boo." -ForegroundColor DarkGray}
Oh noes! You made a boo boo.

Don’t get excited, this doesn’t make any difference. It all boils down to another small fact – the remote session you have opened with Exchange is run in ‘No Language‘ mode. This means no variables, no script blocks, no assignments, nothing at all, but the pipeline. No tab completion either. So while you can get the error handling to work when providing the identity of the mailbox as string, even passing a simple variable to the script block in Invoke-Command will not work due to the ‘No language’ mode. It’s completely useless.

Until the PowerShell team sits down with the Exchange team and give us a better option, don’t feel bad about not following the best practices when it comes to error handling.

This entry was posted in Exchange Online, PowerShell. Bookmark the permalink.

5 Responses to Error handling in Exchange Remote PowerShell sessions

  1. FrenchSpeaker says:

    And here I am thinking I’m doing something wrong with my Try-Catch for weeks… I’m trying to get the Mailbox properties for users in a CSV file & export the results (all is working fine), but those where the mailbox is not in the cloud, obviously got this ugly red error message all other the PS console.

    At this point in time (Jan 2017), is there something we can do to log those errors in another file for users where the the mailbox is not in O365?
    If not, is there a way to avoid displaying this error?

    Thanks!

    • Christophe Niel says:

      I’m just answering on “is there a way to avoid displaying this error?” : yes
      “Get-Mailbox non-existant-mailbox -ErrorAction SilentlyContinue” will simply not display anything
      (-ea for short instead of -ErrorAction, thers is also -warningaction)

      if you pipe multiple command that could all give an error, you’ll need a “-ErrorAction” for each command
      “get-mailbox -ea silentlycontinue |get-mailboxstatistics -ea silentlycontinue”
      you also have a global variable $ErrorActionPreference that you can set to SilentlyContinue (but that’s not really a good idea most of the time.)

  2. Vasil Michev says:

    What I usually do is to put the result in a variable and check against it. For example if I’m looping over a list of users, I’d do something like:

    $checkuser = Get-Mailbox $user -ErrorAction SilentlyContinue

    if (!$checkuser) { Write-Error “User $user not found”; continue }

    where of course you’d replace Write-Error with the appropriate action or remove it altogether if you don’t want to clutter the output with all the error messages.

    • Christophe Niel says:

      eh, now try doing that on office365 with a “restricted language” active, meaning you CANNOT use any variables at all.

      Nice article, but I’m personally stuck in trying to get the warning and error message from a “invoke-command -asJob ….” on a o365 exchange session
      equivalent of a remote start-job, and tricky on the try-catch block…

  3. Pingback: Using variables with Invoke-Command in Remote PowerShell sessions in Exchange Online | Blog

Leave a Reply to Christophe Niel Cancel reply

Your email address will not be published. Required fields are marked *