MSEndpointMgr
Home ยป Azure ยป Azure Automation ยป Exchange Online PowerShell with MFA enforced using Azure Automation

Exchange Online PowerShell with MFA enforced using Azure Automation

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.

Service Principals with Certificates to the rescue!

A picture of some Exchange Online PowerShell

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: https://www.powershellgallery.com/packages/AzureAutomationAgainstExchangeOnlineWithMFAEnabledAccount/1.0.0

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

How to – Azure Automation

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 = "mydomain.onmicrosoft.com"
Connect-ExchangeOnline -CertificateThumbprint $connection.CertificateThumbprint -AppId $connection.ApplicationID -Organization $tenantName

In the above code, you would replace “mydomain.onmicrosoft.com” 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.

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 Role
	By @michael_mardahl - msendpointmgr.com
	Credits for parts of API Permissions script go to adamtheautomator.com
#>
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 Service 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 Registration
	By @Michael_Mardahl - MSEndpointMgr.com
#>

$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 Az
if (-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 file
Export-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 = "mydomain.onmicrosoft.com"
#Requires -Module ExchangeOnlineManagement
Connect-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!

(848)

Michael Mardahl

Michael works as a Microsoft Certified Cloud Architect with APENTO in Denmark. He is specializing in customer journeys from classic Infrastructure to Cloud consumption with a strong focus on security. And has now been in the IT industry for more than 20 years, where he started as a Network Administrator. He has gained experience through a broad range of IT projects. 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.

4 comments

  • Sweet, and if I understand correctly each cmdlet/module (Connect-exchangeonline, connect-msgraph, connect-azuread and so on) needs to have corresponding -CertificateThumbprint as an option for this to work? I mean, even though this works unattended for Exchange online today if I wanted to do the same with the Intune Powershell SDK or AzureAD the engineers at MS would need to incorporate support for certbased auth there aswell?

Sponsors

Subscribe

Do you want to be notified of new posts on our site?

Please enter your email address below:

Categories

MSEndpointMgr.com use cookies to ensure that we give you the best experience on our website.