Exchange Online PowerShell with MFA enforced using Azure Automation (2022)

Home » Azure » Azure Automation » Exchange Online PowerShell with MFA enforced using Azure Automation

Michael Mardahl



7 min read

Secure unattended PowerShell against Exchange Online in Azure Automation using Certificate access.

Securely accessing Exchange Online PowerShell with privileged access (Exchange Administrator) – while MFA is enforced through Conditional Access? It has been a real headache for many admins!

For scheduled tasks or Azure Automation, connecting to Exchange Online PowerShell is a must for any scripted solution!

But with new security measures like Conditional Access. And MFA enforcement coming into their rightful place in most organizations, many of these scripts have broken.

A quick fix is just to exclude the account. Or set up conditions in Conditional Access to allow a non MFA connection for unattended scripts.

But connecting without exclusions and keeping the enforcement in place – is something that has driven many admins to tears.

UPDATE: Here is a requested example of how to do this with Managed Identities, which is a much better way.
ExchangeOnlineScripts/ConnectEXOwithMSIRunbookExample.ps1 at main · mardahl/ExchangeOnlineScripts (

Service Principals with Certificates to the rescue!

Exchange Online PowerShell with MFA enforced using Azure Automation (2)

Service Principals or App registrations in Azure AD are secure modern authentication entities. They can give applications access to Microsoft Online Services and more!

This article will try to sum up what you should do to get a working secure connection to Exchange Online PowerShell. But it is written with the expectation that you are very familiar with PowerShell and Azure AD App Registrations.

I used to have a hacky workaround for this with a script in the PowerShell Gallery. And it is still there, but not needed anymore!

Here is the link to the now useless PowerShell Gallery script:

The official ExchangeOnlineManagement V2 module now supports the use of Certificate-Based Authentication with Service Principals!

How to – Azure Automation

Exchange Online PowerShell with MFA enforced using Azure Automation (3)

If you created your Azure Automation account with a “RunAs” account, it would already have a Service Principal with a certificate (that expires every year btw!).

This article won’t deal with the configuration of Azure Automation, but we cover that in several other articles like this one: Configuring Azure Automation.

So with A RunAs account in Azure Automation, you would first need to install the ExchangeOnlineManagement PowerShell Module into your Azure Automation Account.

Then you can start a new Runbook that can do all sorts of Exchange Online Magic – with this bit of PowerShell code:

$connection = Get-AutomationConnection -Name AzureRunAsConnection$tenantName = ""Connect-ExchangeOnline -CertificateThumbprint $connection.CertificateThumbprint -AppId $connection.ApplicationID -Organization $tenantName

In the above code, you would replace “” with your actual tenant name (Tenant ID is not supported!).

But the above code would be worthless without having done the following steps.

Configure the Service Principals permissions for unattended Exchange Online admin access

There are two things that need to be done in order for your Service Principal to have the correct access to Exchange Online PowerShell. If you don’t grant these permissions you will get access denied when connecting with the certificate.

  1. It needs to be a member of the Exchange Administrators Role
  2. It needs to have Application permissions with full access to Exchange Online.
Exchange Online PowerShell with MFA enforced using Azure Automation (4)

This bit of PowerShell should take care of the permissions – you can run it with the Azure Cloud Shell – like I did!

You will need to have the ID of the App Registration handy and replace it in the code below!

<#Script to add service principal to Azure AD RoleBy @michael_mardahl - msendpointmgr.comCredits for parts of API Permissions script go to>Connect-AzureAD$appId = "b4xxxxe8-xxee-4a22-axx8-6xxxxxxx0d50"$servicePrincipal = Get-AzureADServicePrincipal -Filter "AppID eq `'$appId'"#Adding Role membership$roleDefinition = Get-AzureADDirectoryRole | Where-Object {$_.DisplayName -eq 'Exchange Administrator'}Add-AzureADDirectoryRoleMember -ObjectId $roleDefinition.ObjectId -RefObjectId $servicePrincipal.ObjectId#Adding API Permissions$api = (Get-AzureADServicePrincipal -Filter "AppID eq '00000002-0000-0ff1-ce00-000000000000'")$permission = $api.AppRoles | Where-Object { $_.Value -eq 'Exchange.ManageAsApp' }$apiPermission = [Microsoft.Open.AzureAD.Model.RequiredResourceAccess]@{ ResourceAppId = $api.AppId ; ResourceAccess = [Microsoft.Open.AzureAD.Model.ResourceAccess]@{ Id = $permission.Id ; Type = "Role" }}$Application = Get-AzureADApplication | Where-Object {$_.AppId -eq $appId}$Application | Set-AzureADApplication -ReplyUrls 'http://localhost'$Application | Set-AzureADApplication -RequiredResourceAccess $apiPermission

After the code has been run, you must go to the App registration page in Azure AD and grant admin consent so you can successfully connect to Exchange Online.

That’s it for getting up and running with Azure Automation against Exchange Online!

How to – Scheduled Tasks

If you are still running some of these scripts from On-Prem, I beg you to consider using Azure Automation. It even has a free option that is usually good enough for most small to midsize companies.

If you must use the task scheduler – for whatever reason. You must create the App Registration manually in Azure AD, and assign the permissions to it as described in the above PowerShell script.

You would then need to upload a certificate to be used during authentication. Luckily this can be a self-signed certificate!

This script can create the certificate on your local machine and upload it to the App Registration. You would then have access to Exchange Online PowerShell from your local machine because you need the corresponding private key. The Private key will be placed on the computer generating the certificate (you can export this to another machine if you like, using certlm.msc).

Create a self-signed certificate for app registration authentication

<# Script to create self-signed 10 year valid cert and upload to App RegistrationBy @Michael_Mardahl ->$appId = "b4xxxxe8-xxee-4a22-axx8-6xxxxxxx0d50" # App Id from Azure AD that needs certificate auth$PfxCertPath = '.\MyAppAuth.pfx' #Place to store temporary cert file$CertificatePassword = '1234SecurePassword' #A password you choose to save the cert with$certificateName = 'AZEXOAutomateCert' #A certificate name you choose$ErrorActionPreference = 'Stop'#Requires -Module Azif (-not (Get-AzSubscription)){ try { Connect-AzAccount } catch { $_; exit 1 } } try {#Creating secure password string$SecurePassword = ConvertTo-SecureString -String $CertificatePassword -AsPlainText -Force#Creating 10 year valid self-signed cert$NewCert = New-SelfSignedCertificate -CertStoreLocation Cert:\LocalMachine\My ` -DnsName $certificateName ` -Provider 'Microsoft Enhanced RSA and AES Cryptographic Provider' ` -KeyAlgorithm RSA ` -KeyLength 2048 ` -NotAfter (Get-Date).AddYears(10)#Exporting cert to fileExport-PfxCertificate -FilePath $PfxCertPath ` -Password $SecurePassword ` -Cert $NewCert -Force} catch { $_ exit 1}#Configure required flags on the certificate$flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable ` -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet ` -bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet# Load the certificate into memory$PfxCert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($PfxCertPath, $CertificatePassword, $flags) -ErrorAction Stop#Upload cert to Azure App Registration$binCert = $PfxCert.GetRawCertData() $certValue = [System.Convert]::ToBase64String($binCert)New-AzADAppCredential -ApplicationId $appId -CertValue $certValue -StartDate $PfxCert.NotBefore -EndDate $PfxCert.NotAfter

You will need the thumbprint of the certificate to add to the following script, which should be at the start of your Scheduled Task PowerShell script. The thumbprint can be viewed in many ways, but the easy way to make sure you have the right one is to just look in the App Registration in Azure, and click on the “Certificates and Secrets” menu item – it will be right there.

$certThumbprint = "XXXXXXXXXXXXXXXXXXXX"$appId = "XXXXXXXXXXXXXXX"$tenantName = ""#Requires -Module ExchangeOnlineManagementConnect-ExchangeOnline -CertificateThumbprint $certThumbprint -AppId $appId -Organization $tenantName

That’s it for tips on how to get this working with Scheduled tasks!

Final words

Microsoft has come along way with the new PowerShell modules. However, granularity on the backend is still missing, and the true graph experience for Exchange Automation is still not there yet… We can only dream for now!

Please don’t hesitate to follow and reach out on LinkedIn and Twitter to discuss this and more automation goodness.

Thanks for reading!


Azure Automation Exchange Online Modern Authentication PowerShell

Exchange Online PowerShell with MFA enforced using Azure Automation (5)

Michael Mardahl

Michael works as a Microsoft Certified Cloud Architect with APENTO in Denmark. He specializes in customer journeys from classic Infrastructure to Cloud consumption with a strong focus on security. And has been working in the IT industry for more than 20 years, where he started as a Network Administrator in the logistics industry. He has gained experience through a broad range of IT projects throughout the years and was very early to embrace and share his cloud technology passion. When not at work, Michael enjoys the value of spending time with family and friends and BLOG's passionately about Microsoft cloud technology whenever he has time to spare - this has earned him the title of Microsoft Most Valuable Professional (MVP) in the Enterprise Mobility category.

View all posts

Top Articles

You might also like

Latest Posts

Article information

Author: Greg Kuvalis

Last Updated: 12/23/2022

Views: 5703

Rating: 4.4 / 5 (75 voted)

Reviews: 90% of readers found this page helpful

Author information

Name: Greg Kuvalis

Birthday: 1996-12-20

Address: 53157 Trantow Inlet, Townemouth, FL 92564-0267

Phone: +68218650356656

Job: IT Representative

Hobby: Knitting, Amateur radio, Skiing, Running, Mountain biking, Slacklining, Electronics

Introduction: My name is Greg Kuvalis, I am a witty, spotless, beautiful, charming, delightful, thankful, beautiful person who loves writing and wants to share my knowledge and understanding with you.