Using Managed Identities to Connect to Microsoft 365 & Azure
Overview
Managed identities for Azure Automation runbooks offer a secure and maintenance-free approach to authenticate against Microsoft 365 and Azure cloud services. This blog post provides the ultimate guidance how to make your Azure Automation accounts ready for managed identities and how to connect to
- Azure
- Azure AD
- Microsoft Graph
- Exchange Online
- SharePoint Online
- OneDrive for Business
- Microsoft Teams
in your PowerShell runbooks.
Enable the Managed Identity for Azure Automation Runbooks
In the “Identity” menu of your Azure Automation account, you can activate the managed identity. When talking about Azure runbooks, I recommend to use system-assigned managed identities since they are bound to the lifecycle of the associated Automation account and they introduce less complexity and less maintenance efforts.
By default, the newly created managed identity does not receive any CRUD permissions in your Microsoft cloud environment. Instead, you need to assign appropriate API permissions or Azure AD roles. Below in this blog post, I will show you how this is done for each of the Microsoft 365 and Azure workloads.
Import necessary PowerShell Modules
Depending on your use case, the “Modules” menu of your Automation account allows to upload further PowerShell modules. If you just need to import the most recent module version, please click on “Browse gallery” and use the search bar. ExchangeOnlineManagement, PnP.PowerShell and MicrosoftTeams are available in the integrated gallery, for example.
In some cases, you may need to import specific versions or pre-release versions. For example, the managed identity support in the Microsoft.Graph PowerShell SDK starts in v2.0.0, which is currently only available as pre-release. Here, you can leverage the “Deploy to Azure Automation” button on PowerShellGallery.com to easily import a supported version into your Automation account. For example, we can deploy version 2.0.0-preview5 of the “Microsoft.Graph.Authentication” package:
Azure
The following code sample demonstrates how to apply managed identity-based authentication with the Az module and thus how to programmatically access Azure resources such as VMs:
Disable-AzContextAutosave -Scope Process
$azureConnection = Connect-AzAccount -Identity
$azureContext = $azureConnection.context
$azureContext = Set-AzContext `
-SubscriptionName $azureContext.Subscription `
-DefaultProfile $azureContext
# Example: Get the VMs in the specified Azure resource group
$vms = Get-AzVM `
-ResourceGroupName "{RESOURCE-GROUP-NAME}" `
-DefaultProfile $azureContext
Disconnect-AzAccount
As a best practice, please take care to avoid unintended context switching as indicated in the example above.
The leveraged cmdlets originate from the Az modules, which are already available in Azure Automation accounts by default. Consequently, there is no prerequisite for a manual module import before executing the runbook.
Nevertheless, the managed identity requires appropriate Azure roles to perform CRUD operations on resources in your Azure subscription. The Microsoft documentation provides good guidance how to grant roles such as Reader, Contributor or Owner to system-assigned managed identities.
Azure Active Directory
As written in my previous blog post Migrating PowerShell Scripts from AzureAD to Microsoft.Graph, the AzureAD module will retire soon and you should move towards the Microsoft Graph-based approach to programmatically access user, group or any other directory objects in your tenant.
Consequently, please check out the following section on Microsoft Graph explaining how to use a managed identity in combination with the modern MS Graph API.
Microsoft Graph
Starting with version 2 of Microsoft Graph PowerShell SDK, authentication with system-assigned managed identities is supported by applying the -Identity
switch parameter when initializing the connection:
Connect-MgGraph -Identity
# Example: Fetch all users but only request certain profile properties
$guests = Get-MgUser `
-All `
-Property Id, UserPrincipalName, Mail, AccountEnabled `
-Expand "manager(`$select=id,userPrincipalName,mail)" `
| Select-Object Id, UserPrincipalName, Mail, AccountEnabled, Manager
Disconnect-MgGraph
As a prerequisite to read or manipulate tenant data via MS Graph API, you need to assign appropriate OAuth 2.0 application permissions to the managed identity. The following code sample illustrates how to grant User.Read.All permissions:
$managedIdentityId = "{OBJECT-ID-OF-YOUR-MANAGED-IDENTITY}"
Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory
# Grant User.Read.All permissions
$graphResource = Get-MgServicePrincipal `
-Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
$userReadRole = $graphResource.AppRoles `
| Where-Object {$_.Value -eq "User.Read.All"}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $managedIdentityId `
-PrincipalId $managedIdentityId `
-ResourceId $graphResource.Id `
-AppRoleId $userReadRole.Id
Disconnect-MgGraph
Since v2.0.0 of the Graph PowerShell modules are currently only published as pre-release, you can visit PowerShellGallery.com to deploy the correct packages into your Azure Automation account. Please use the above description in this blog post as a reference how to perform the import. At the very least, the “Microsoft.Graph.Authentication” module is required. Depending on your scenario, you should import additional packages such as “Microsoft.Graph.Users”.
Exchange Online
Taking advantage of the -ManagedIdentity
switch parameter in the EXO V3 module, you can open a connection to Exchange Online in your Azure runbooks:
$organization = "{TENANT-NAME}.onmicrosoft.com"
Connect-ExchangeOnline -ManagedIdentity -Organization $organization
# Example: Fetch all mailboxes
$mailboxes = Get-EXOMailbox
Disconnect-ExchangeOnline -Confirm:$false
In order to read or write Exchange resources, the Exchange.ManageAsApp permission as well as an appropriate Azure AD role like Exchange Administrator must be assigned to the managed identity. These prerequisites can be achieved with the subsequent snippet:
$managedIdentityId = "{OBJECT-ID-OF-YOUR-MANAGED-IDENTITY}"
Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory
$exchangeResource = Get-MgServicePrincipal `
-Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
# Grant Exchange.ManageAsApp permissions
$exchangeManageAsAppRoleId = "dc50a0fb-09a3-484d-be87-e023b12c6440"
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $managedIdentityId `
-PrincipalId $managedIdentityId `
-ResourceId $exchangeResource.Id `
-AppRoleId $exchangeManageAsAppRoleId
# Assign Exchange Administrator role
$exchangeAdminRole = Get-MgRoleManagementDirectoryRoleDefinition `
-Filter "DisplayName eq 'Exchange Administrator'"
New-MgRoleManagementDirectoryRoleAssignment `
-PrincipalId $managedIdentityId `
-RoleDefinitionId $exchangeAdminRole.Id `
-DirectoryScopeId "/"
Disconnect-MgGraph
Microsoft documents the limitation that Microsoft 365 Groups (previously called Office 365 Groups or Unified Groups) cannot be managed through the managed identity approach in ExchangeOnlineManagement shell. Instead, Microsoft Graph and the MgGroup*
cmdlets are the way to go.
Unfortunately, the Security & Compliance PowerShell also does not support managed identity authentication yet, so we cannot execute the Connect-IPPSSession
command after the Exchange Online connection has been established.
SharePoint Online
The PnP.PowerShell module delivers the best capabilities to perform CRUD operations against SharePoint Online and we can utilize the -ManagedIdentity
switch when opening the connection to SPO:
$tenantName = "{TENANT-NAME}"
$tenantAdminUrl = "https://$tenantName-admin.sharepoint.com"
Connect-PnPOnline -Url $tenantAdminUrl -ManagedIdentity
# Example: Fetch all SharePoint sites (including M365 Groups and MS Teams connected sites)
$sites = Get-PnPTenantSite
Disconnect-PnPOnline
As a prerequisite, application-level API permissions like Sites.FullControl.All or Sites.Selected must be assigned to the managed identity. For this purpose, please check out this code example:
$managedIdentityId = "{OBJECT-ID-OF-YOUR-MANAGED-IDENTITY}"
Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory
# Grant Sites.FullControl.All permissions
$sharePointResource = Get-MgServicePrincipal `
-Filter "AppId eq '00000003-0000-0ff1-ce00-000000000000'"
$sitesFullControlAllRole = $sharePointResource.AppRoles `
| Where-Object {$_.Value -eq "Sites.FullControl.All"}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $managedIdentityId `
-PrincipalId $managedIdentityId `
-ResourceId $sharePointResource.Id `
-AppRoleId $sitesFullControlAllRole.Id
Disconnect-MgGraph
OneDrive for Business
The PnP.PowerShell module provides the capability to authenticate through managed identities and access OneDrive sites in your tenant:
$tenantName = "{TENANT-NAME}"
$tenantAdminUrl = "https://$tenantName-admin.sharepoint.com"
Connect-PnPOnline -Url $tenantAdminUrl -ManagedIdentity
# Example: Fetch all OneDrive personal sites
$oneDriveSites = Get-PnPTenantSite `
-IncludeOneDriveSites `
-Filter "Url -like 'https://$tenantName-my.sharepoint.com/personal/'"
Disconnect-PnPOnline
For more details regarding necessary configurations like required API permissions, please check out the section on SharePoint Online.
Microsoft Teams
There are two options how to access the Microsoft Teams service in your Azure Automation runbooks: the PnP.PowerShell or the MicrosoftTeams module. Whenever possible, I recommend to try the PnP cmdlets first because the PnP developer community is more active and the PnP modules are typically more up-to-date regarding new changes in Microsoft’s APIs. In particular, PnP is the best choise when it comes to the management of teams, channels, tabs and other front-end MS Teams resources. In contrast, the MicrosoftTeams package is required when dealing with administrative Teams policies.
In the following PowerShell snippet, we are authenticating via managed identity and fetching all teams in Microsoft Teams by leveraging PnP.PowerShell. Analogously, you could also read, create, update or delete teams, channels, tabs, members etc. with PnP.
$tenantName = "{TENANT-NAME}"
$tenantAdminUrl = "https://$tenantName-admin.sharepoint.com"
Connect-PnPOnline -Url $tenantAdminUrl -ManagedIdentity
# Example: Fetch all Microsoft Teams teams
$teams = Get-PnPTeamsTeam
Disconnect-PnPOnline
The exact same scenario looks as follows when utilizing the MicrosoftTeams module:
Connect-MicrosoftTeams -Identity
# Example: Fetch all Microsoft Teams teams
$teams = Get-Team
Disconnect-MicrosoftTeams -Confirm:$false
Typically, the Group.ReadWrite.All application permission is necessary if you want to read properties of existing teams, channels, etc. and if you want to create new teams, add channels, invite members into teams, or similar. Depending on your use case, it might be sufficient to assign a more granular app role such as TeamMember.Read.All / TeamSettings.ReadWrite.All / TeamsTab.Create according to the Microsoft Graph API permission reference. The following code snippet offers an example how to grant broad Teams access to the managed identity of your Azure Automation account:
$managedIdentityId = "{OBJECT-ID-OF-YOUR-MANAGED-IDENTITY}"
Connect-MgGraph -Scopes Application.Read.All, AppRoleAssignment.ReadWrite.All, RoleManagement.ReadWrite.Directory
# Grant Group.ReadWrite.All permissions
$graphResource = Get-MgServicePrincipal `
-Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
$groupReadWriteRole = $graphResource.AppRoles `
| Where-Object {$_.Value -eq "Group.ReadWrite.All"}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $managedIdentityId `
-PrincipalId $managedIdentityId `
-ResourceId $graphResource.Id `
-AppRoleId $groupReadWriteRole.Id
Disconnect-MgGraph
Talking about limitations, you may want to manage administrative Teams policies such as meeting, calling, audio conferencing or external access policies in your runbooks as well. This is where the Cs*
cmdlets from the legacy Skype for Business system come in, which are exposed by the MicrosoftTeams module by now. Unfortunately, while Microsoft has announced support for application-based authentication in Teams PowerShell since version 4.7.1, the MicrosoftTeams module does not seem to provide support for managed identity-based authentication for the Cs*
cmdlets. During my tests with version 5.0.0 from March 2023, the execution of Connect-MicrosoftTeams -Identity
plus an arbitrary Skype cmdlet like Get-CsTenant
or Get-CsTeamsCallingPolicy
resulted in a “(404) Not Found” error message. So for these scenarios, you are required to create a custom app registration instead of a managed identity.