Exchange Online and Azure Automation

In a previous article, I explore how to use the Microsoft Teams and Microsoft Graph PowerShell SDK modules with Azure Automation managed identities. At the time, I noted that Exchange Online didn’t support managed identities. At least, the current production version of the module (2.0.5 at the time of writing) doesn’t. Some joy appears to be on the horizon as the notes for V2.0.6-Preview7 of the Exchange Online management module says that it includes “support for system-assigned and user-assigned Managed Identity from Azure VMs and Virtual Machine Scale Sets.”

The notes also say:

  • “The -ManagedIdentity switch parameter, and the -Organization parameters need to be provided to indicate that a managed identity should be used. This will by default attempt to use a system-assigned managed identity.
  • For specifying a user-assigned managed identity, in addition to the parameters specified above, the AppID of the service principal corresponding to the user-assigned identity needs to be passed to the -ManagedIdentityAccountId.”

Exchange Online Management 3.0

Update (September 20, 2022): Microsoft has released V3.0 of the Exchange Online Management module, complete with support for Managed Identities. My initial tests show that connecting to Exchange Online with a managed identity using a command like that shown below works nicely.

Connect-ExchangeOnline -ManagedIdentity -Organization office365itpros.onmicrosoft.com

The service principal belonging to the managed identity still needs the permission to perform Exchange Online management actions. I did note that it isn’t possible to create a new Microsoft 365 group with the New-UnifiedGroup cmdlet. This might be a temporarily glitch, or perhaps this cmdlet is not yet capable of working with a managed identity.

Using Managed Identities with Exchange PowerShell V1

While we wait for Microsoft to deliver a production module, it’s possible to use the older V1 Exchange module with a managed identity. MVP Vasil Michev lays out how in his blog. Before going further, it’s important to say that Microsoft doesn’t support this method either. It works, but if you use it in production, don’t expect to be able to call Microsoft support if things go wrong.

The important step in the process is to assign the Manage Exchange As Application role to the service principal of the managed identity you choose to use. This is done by:

  • Getting the details of the Microsoft enterprise app registered for Exchange management.
  • Fetching the permission identifier for the Manage Exchange as Application role from the set of roles defined for the Exchange management app.
  • Using the information to assign the Manage Exchange as Application role to the service principal for the managed identity.

Once again, PowerShell is the only way to make the assignment. Here’s the code I used:

$ExoApp = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
$AppPermission = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}

$AppRoleAssignment = @{
    "PrincipalId" = $ManagedIdentityApp.Id
    "ResourceId" = $ExoApp.Id
    "AppRoleId" = $AppPermission.Id
  }
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id -BodyParameter $AppRoleAssignment

Once the service principal holds the role allowing it to act as an Exchange administrator, we can put it to work. This code is an example of using the managed identity to sign into Exchange Online using an access token generated for the identity (using a special account called OAuthUser@ plus the tenant identifier (OAuthUser@a662313f-14fc-43a2-9a7a-d2e27f4f3478 in this instance). After connecting, we run some standard Exchange PowerShell code to find all user mailboxes and return some mailbox statistics.

# Get token with Service Principal for managed identity
$ResourceURI = "https://outlook.office365.com/"
$TokenAuthURI = $env:IDENTITY_ENDPOINT + "?resource=$resourceURI&api-version=2019-08-01"
$TokenResponse = Invoke-RestMethod -Method Get -Headers @{"X-IDENTITY-HEADER"="$env:IDENTITY_HEADER"} -Uri $tokenAuthURI
$AccessToken = $TokenResponse.access_token
$Authorization = "Bearer {0}" -f $accessToken 
$Password = ConvertTo-SecureString -AsPlainText $Authorization -Force

$Ctoken = New-Object System.Management.Automation.PSCredential -ArgumentList "OAuthUser@a662313f-14fc-43a2-9a7a-d2e27f4f3478",$Password
 
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/PowerShell-LiveId?BasicAuthToOAuthConversion=true -Credential $Ctoken -Authentication Basic -AllowRedirection -Verbose
Import-PSSession $Session | Format-List

$Mbx = Get-Mailbox -RecipientTypeDetails UserMailbox -ResultSize Unlimited
$MbxList = [System.Collections.Generic.List[Object]]::new()
ForEach ($M in $Mbx) {
   $Stats = Get-MailboxStatistics -Identity $M.DistinguishedName
   $MLine = [PSCustomObject][Ordered]@{  # Write out details of the private channel and its members
          Mailbox             = $M.DisplayName
          UPN                 = $M.UserPrincipalName
          Items               = $Stats.ItemCount
          Size                = $Stats.TotalItemSize
          LastInteraction     = $Stats.LastInteraction
          ProhbitSendQuota    = $M.ProhibitSendReceiveQuota }
       $MbxList.Add($MLine) }
$MbxList | Sort-Object Items -Descending | Format-Table Mailbox, Items, Size
Remove-PSSession $Session

The code works (Figure 1) with the big caveat that only the old cmdlets (the set that originated on-premises) are available. None of the newer REST-based cmdlets found in the Exchange Online management module (like Get-EXOMailbox) can be used.

Running Exchange Online PowerShell with a managed identity
Figure 1: Running Exchange Online PowerShell with a managed identity

Using the Exchange Online V2 Module

If you want to use the cmdlets in the Exchange Online V2 module to take advantage of their speed and reliability when fetching large amounts of objects, you can, but not with a managed identity (for now). Instead, you could use some of the techniques explored in previous articles, notably by storing username and password credentials in Azure Key Vault. The account must hold the Exchange administrator role to be able to perform Exchange administrative tasks. This might be an interim stage to allow the automation of some repetitive periodic processing while waiting for Microsoft to perfect managed identity support for Exchange Online.

Take the script I wrote to check shared mailboxes for appropriate licenses. Normally, shared mailboxes don’t need licenses, but once a shared mailbox stores more than 50 GB of content, has an archive, or is on litigation hold, the rules change and the mailbox needs an Exchange Online Plan 2 license. You can ignore the requirement, but eventually Exchange will stop the mailbox from receiving and sending emails, and that’s a bad place to be.

The script uses the Get-ExoMailbox and Get-ExoMailboxStatistics cmdlets to interact with mailboxes and the Get-MgUserLicenseDetail cmdlet (from the Microsoft Graph PowerShell SDK) to check the licensing status of the Azure AD accounts that own the shared mailboxes.

To get the script running in Azure Automation, I included the code to sign into the Microsoft Graph PowerShell SDK with a managed account and fetched the account credentials from Azure Key Vault. Here’s the code to retrieve the credentials and connect to Exchange:

# Connect to AzAccount to access Key Vault to fetch variables used by the script
$AzConnection = Connect-AzAccount -Identity | Out-Null
# Get username and password from Key Vault
$UserName = Get-AzKeyVaultSecret -VaultName "Office365ITPros" -Name "ExoAccountName" -AsPlainText
$UserPassword = Get-AzKeyVaultSecret -VaultName "Office365ITPros" -name "ExoAccountPassword" -AsPlainText
# Create credentials object from the username and password
[securestring]$SecurePassword = ConvertTo-SecureString $UserPassword -AsPlainText -Force
[pscredential]$UserCredentials = New-Object System.Management.Automation.PSCredential ($UserName, $SecurePassword)
Connect-ExchangeOnline -Credential $UserCredentials

After that, it was just a matter of updating the script code slightly to change some details (like removing some instances of Write-Host and updating others to use Write-Output instead – there’s no point in including lots of informative output when a script executes in Azure Automation). I also included a Disconnect-ExchangeOnline command in the script to remove the connection to Exchange Online when the script finishes. This avoids encountering problems when running the script multiple times in quick succession during testing as Azure Automation doesn’t allow more than three concurrent connections.

Running a script using the Exchange Online V2 PowerShell module in Azure Automation

Azure Automation managed identity
Figure 2: Running a script using the Exchange Online V2 PowerShell module in Azure Automation

As I expected, everything worked without a hitch (Figure 2). I didn’t go through the process of creating suitable output for consumption by administrators. This could be done by using any of these well-known methods:

Next Step – Production

Of the two methods, I favor using the second as I’d always prefer to use the Exchange Online V2 cmdlets. It’s interesting to connect to the V1 modules using a managed identity, but it’s more practical to go with the V2 cmdlets, especially as they’ll be the basis for the production solution. In the interim, fetching credentials from Azure Key Vault isn’t perfect, but it works, and that’s usually the most important thing.

Meet other PowerShell experts at The Experts Conference 2022, December 6-7.

100% Free and Virtual. You don't want to miss the Microsoft training event of the year!

Learn More

About the Author

Tony Redmond

Tony Redmond has written thousands of articles about Microsoft technology since 1996. He is the lead author for the Office 365 for IT Pros eBook, the only book covering Office 365 that is updated monthly to keep pace with change in the cloud. Apart from contributing to Practical365.com, Tony also writes at Office365itpros.com to support the development of the eBook. He has been a Microsoft MVP since 2004.

Comments

  1. Jan

    Hello Toni; maybe a stupid question but what is in $env:IDENTITY_ENDPOINT?
    I don’t have that environment varable.

Leave a Reply