iphase.dk Michael Mardahl, MVP
Exchange Online PowerShell with MFA enforced using Azure Automation
>> The Problem
Securely accessing Exchange Online PowerShell with privileged access (Exchange Administrator) while MFA is enforced through Conditional Access 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 Conditional Access and MFA enforcement, many scripts have broken.
A quick fix is to exclude the account or set up conditions in Conditional Access to allow a non-MFA connection. But connecting without exclusions and keeping enforcement in place is what we really want.
UPDATE: Here is a requested example using Managed Identities, which is a much better approach:
ConnectEXOwithMSIRunbookExample.ps1 (https://github.com/mardahl/ExchangeOnlineScripts/blob/main/AzureAutomation/ConnectEXOwithMSIRunbookExample.ps1)
>> Service Principals with Certificates to the rescue
Service Principals or App registrations in Azure AD are secure modern authentication entities that can give applications access to Microsoft Online Services.
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).
With a RunAs account in Azure Automation, first install the ExchangeOnlineManagement PowerShell Module into your Azure Automation Account.
Then you can start a new Runbook with this code:
>> [powershell]
$connection = Get-AutomationConnection -Name AzureRunAsConnection
$tenantName = "mydomain.onmicrosoft.com"
Connect-ExchangeOnline -CertificateThumbprint $connection.CertificateThumbprint -AppId $connection.ApplicationID -Organization $tenantName
Replace "mydomain.onmicrosoft.com" with your actual tenant name (Tenant ID is not supported).
>> Configure the Service Principal Permissions
Two things need to be done for your Service Principal to have correct access:
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
The following PowerShell script handles the permissions - run it with Azure Cloud Shell:
>> [powershell]
<#
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 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 running, go to the App registration in Azure AD and GRANT ADMIN CONSENT.
>> How to - Scheduled Tasks
If running scripts from On-Prem, consider Azure Automation instead (it has a free option). If you must use task scheduler, create the App Registration manually and assign permissions as above.
>> Create a self-signed certificate for app registration authentication
>> [powershell]
<#
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"
$PfxCertPath = '.\MyAppAuth.pfx'
$CertificatePassword = '1234SecurePassword'
$certificateName = 'AZEXOAutomateCert'
$ErrorActionPreference = 'Stop'
#Requires -Module Az
if (-not (Get-AzSubscription)){ try { Connect-AzAccount } catch { $_; exit 1 } }
try {
$SecurePassword = ConvertTo-SecureString -String $CertificatePassword -AsPlainText -Force
$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)
Export-PfxCertificate -FilePath $PfxCertPath `
-Password $SecurePassword `
-Cert $NewCert -Force
} catch {
$_
exit 1
}
$flags = [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable `
-bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet `
-bor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet
$PfxCert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @($PfxCertPath, $CertificatePassword, $flags) -ErrorAction Stop
$binCert = $PfxCert.GetRawCertData()
$certValue = [System.Convert]::ToBase64String($binCert)
New-AzADAppCredential -ApplicationId $appId -CertValue $certValue -StartDate $PfxCert.NotBefore -EndDate $PfxCert.NotAfter
For the Scheduled Task script, use the certificate thumbprint from the App Registration:
>> [powershell]
$certThumbprint = "XXXXXXXXXXXXXXXXXXXX"
$appId = "XXXXXXXXXXXXXXX"
$tenantName = "mydomain.onmicrosoft.com"
#Requires -Module ExchangeOnlineManagement
Connect-ExchangeOnline -CertificateThumbprint $certThumbprint -AppId $appId -Organization $tenantName
>> Final words
Microsoft has come a long way with the new PowerShell modules. However, granularity on the backend is still missing, and the true Graph experience for Exchange Automation is not fully there yet.
C:\IPHASE\POSTS\AZURE\EXCHAN~1.TXT
1 Help 3 Home 5 About 7 Posts 8 Contact 10 LinkdIn
imagevwr.exe