Maintaining a Microsoft 365 Retention Policy with PowerShell
Problems with the Connect-IPPSSession Cmdlet in Azure Automation
Earlier this week, I discussed how to create a Microsoft 365 retention policy for shared mailboxes. In that article, I noted that additional processing would be needed to keep the retention policy updated as new shared mailboxes are created, mailboxes deleted, and even the conversion of user mailboxes to shared mailboxes and vice versa. This is the kind of scheduled background processing that is well suited to Azure Automation.
At least, that’s what I thought when I started to write the PowerShell script to do the work. The code to run interactively is simple to write. All the problems arise when making the code work in an Azure Automation runbook. The root problem is that the cmdlets in the security and compliance module (the ones accessed by running the Connect-IPPSSession cmdlet) don’t support managed identities like the main Exchange Online management modules does.
Support for managed identities makes running Exchange Online management cmdlets in an Azure Automation runbook a straightforward exercise. Care must be taken to ensure that the service principal for the automation account has the Exchange administrator role and is assigned the Manage Exchange as Application permission. After that, the connection with a managed identity works without a flaw.
The problem is that the developers who maintain the Security and Compliance module haven’t done the work to support managed identities. Microsoft publishes guidance for app-only access for unattended scripts, but the information doesn’t address the environment where Azure Automation runbooks execute. At least, I’ve had zero luck with any of Microsoft’s recommendations.
Back to PowerShell Credentials
Much to my disgust, I had to revert to account credentials passed in a PowerShell credential object. Not only does this seem like the kind of thing we did ten years ago, it means that you can’t use an account that is enabled for multifactor authentication. The only saving grace is that the credential object can be saved in the automation account and doesn’t need to be hardcoded in the script. My doubts were somewhat assuaged by using a long and complex password (Figure 1).

The stored credential can be retrieved in a runbook using the Get-AutomationPSCredential cmdlet and passed to the Connect-IPPSSession cmdlet with the Credential parameter. Here’s the code from the script that checks if the code is running interactively or not. You can see that Connect-ExchangeOnline and Connect-IPPSSession cmdlets run because access is needed to fetch details of shared mailboxes and to update the retention policy.
If ([Environment]::UserInteractive) { # We're running interactively... Write-Host "Script running interactively... connecting to Exchange Online PowerShell" -ForegroundColor Yellow [array]$Modules = Get-Module | Select-Object -ExpandProperty Name If ("ExchangeOnlineManagement" -Notin $Modules) { Write-Output "Connecting to Exchange Online..." Connect-ExchangeOnline -showBanner:$false } Write-Output "Connecting to Security & Compliance Center PowerShell..." Connect-IPPSSession -ShowBanner:$false } Else { # We're not, so likely in Azure Automation Write-Output "Running the update retention policy for Microsoft 365 shared mailboxes in Azure Automation" # Fetch the account credentials to use $O365Cred = Get-AutomationPSCredential -Name "AzureExchangeOperations" Connect-ExchangeOnline -Credential $O365Cred -DisableWAM Connect-IPPSSession -Credential $O365Cred }
The Need to Disable Windows Account Manager
The Connect-ExchangeOnline command uses the DIsableWAM parameter to disable Windows Account Manager. WAM is enabled by default from Exchange Online Management V3.7 onward and must be disabled in environments where normal interactive authentication is not possible. This fact is not widely known. WAM can also be used with the Microsoft Graph PowerShell SDK, but it’s not the default.
If a script only needs to run Connect-IPPSSession without first connecting to Exchange Online in a non-interactive environment, the call to Connect-IPPSSession should include the DisableWAM parameter. Of course, if you only need to connect to Exchange Online, use a managed identity.
Updating the Retention Policy
After solving the connection issue, the rest of the code was simple enough:
- Find the current set of shared mailboxes with Get-ExoMailbox.
- Find the set of shared mailboxes covered by the retention policy with the Get-RetentionCompliancePolicy cmdlet. It’s important to include the DistributionDetail parameter to force the population of targeted locations.
- Add shared mailboxes that aren’t covered by the retention policy.
- Remove any locations that aren’t shared mailboxes (deleted mailboxes or shared mailboxes converted to be user mailboxes).
- Update the retention policy with the Set-RetentionCompliancePolicy cmdlet.
- Remove Exchange MRM retention policies from the new shared mailboxes.
You can download the complete script from the Office 365 for IT Pros repository. Figure 2 shows a successful runbook in Azure Automation. The only thing to do is to add the runbook to an Azure Automation schedule so that it runs at an appropriate interval. Weekly should be sufficient.

Consistency Would be Nice
It would be nice if all the Microsoft 365 PowerShell modules supported managed identities and Azure automation in a constant and predictable manner. The priorities of different engineering groups means that this is not the current case. We can but hope that the situation will improve over time.
Need some assistance to write and manage PowerShell scripts for Microsoft 365? Get a copy of the Automating Microsoft 365 with PowerShell eBook, available standalone or as part of the Office 365 for IT Pros eBook bundle.