Primer: How to Use Azure Automation to Run Microsoft Graph PowerShell SDK Scripts
Running PowerShell in Azure Automation Runbooks Seems Complex – But Is it?
Over this past weekend, I was quizzed about why many people recommend using Azure Automation runbooks to run PowerShell scripts when setting everything up is so complex. I guess that I’ve been using this stuff for so long that I just accept how it works and parse out some of the issues that people struggle with. In an attempt to help, I thought that I’d create a really simple example as a starting point. Let’s see how I can do.
The Nature of Azure Automation
Azure Automation is a cloud-based service that supports running PowerShell scripts on headless servers. The scripts are called runbooks and the code that runs in Azure Automation is similar to the scripts that you run interactively.
The big difference is that Azure Automation is a non-interactive environment. Prompts don’t exist and any output is only seen when scripts finish. That being said, it is not difficult to take code written for interactive use and move it to Azure Automation. In fact, because debugging code in a non-interactive environment is difficult, it’s always best to make sure that a script runs without problems in an interactive environment before attempting to move it to Azure Automation.
Starting Off with Azure Automation
To begin, you’ll need an Azure subscription with an associated credit card to pay for the resources used to run code. Microsoft has Azure free account and pay-as-you-go options.
With an Azure account, you can create a resource group (to hold the resources needed by Azure Automation) and an automation account (Figure 1). The automation account holds the permissions and roles needed to access Microsoft 365 data.

Resources for the Automation Account
Before writing any code to access Microsoft 365 via Azure Automation, you’ll need to add some resources to the automation account. The resources are the PowerShell modules containing the cmdlets needed by your scripts. When you execute a runbook, Azure Automation loads the modules into the session created on the headless server.
To add a PowerShell module, access the automation account and go to Modules under Shared Resources. Click Browse gallery and input the name of the module to add (Figure 2).This example features a script to list recently created Entra ID user accounts, so I added the Microsoft.Graph.Authentication (needed for any Graph SDK script) and the Microsoft.Graph.Users modules.

How do you know what modules to add? In some cases, like Exchange Online, a single module (ExchangeOnlineManagement) is needed. The Microsoft Graph PowerShell SDK is more complex because it’s composed of multiple sub-modules. An easy way to find out which sub-module is needed for a specific cmdlet is to use Get-Command in an interactive session. For instance, Get-Command reports that the source for the Get-MgUser cmdlet is the Microsoft.Graph.Users module:
Get-Command Get-MgUser | Format-Table Name, Source Name Source ---- ------ Get-MgUser Microsoft.Graph.Users
Permissions for the Automation Account
As you’re probably aware, any access to data via the Microsoft Graph is governed by permissions. Automation accounts use application permissions and therefore have access to any data in the tenant allowed by the assigned permissions.
The permissions also include Entra ID roles needed to access the data you want to process. For instance, cmdlets from the Exchange Online module assume that they’re run by administrators, so the automation account must be added to the Entra ID Exchange administrator role and have consent for the Exchange ManageAsApp permission.
The permissions granted to automation accounts are held by the service principal for each account. You can see details of the service principal for an automation account in the enterprise apps section of the Entra admin center. Figure 3 shows that the automation account called M365Automation has a single assigned Graph permission (User.Read.All).

The Entra admin center allows you to see assigned permissions but not assign other permissions. You can only assign permissions with PowerShell. This is a little messy, but once you know how, it will make sense. First, find the details of the Graph application (which always has the same identifier) and the service principal for the automation account.
$GraphApp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'" $TargetSP = Get-MgServicePrincipal -filter "displayname eq 'M365Automation'"
Next, find the identifier for the app role (permissions) we want to assign.
$Role = $GraphApp.AppRoles | Where-Object {$_.Value -eq "User.Read.All"}
Now build a hash table containing the parameters for the new role assignment. As you can see, the parameters are the identifiers for the service principal, resource (Microsoft Graph), and the role.
$AppRoleAssignment = @{} $AppRoleAssignment.Add("PrincipalId",$TargetSP.Id) $AppRoleAssignment.Add("ResourceId",$GraphApp.Id) $AppRoleAssignment.Add("AppRoleId",$Role.Id)
Finally, run the New-MgServicePrincipalAppRoleAssignment cmdlet to make the assignment and report success if an application role assignment identifier is returned.
$RoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $TargetId -BodyParameter $AppRoleAssignment If ($RoleAssignment.AppRoleId) { Write-Host ("{0} permission granted to {1}" -f $Role.Value, $TargetSP.DisplayName) }
Write Some Code for an Azure Automation Runbook
All the steps above have created the environment to write and run some PowerShell code. My example is to return the names of Entra ID accounts created in the last month. In an interactive session, the code is:
$Date = (Get-Date).ToUniversalTime().AddDays(-30).ToString("yyyy-MM-ddTHH:mm:ssZ") [array]$Users = Get-MgUser -Filter "createdDateTime ge $Date" -Property Id, displayName, UserType, CreatedDateTime |Sort-Object UserType If ($Users) { $Users | Format-Table DisplayName, UserType }
The same code works in Azure Automation. Go to the automation account and create a PowerShell runbook based on V7.2. Copy the same code into the runbook and add a line to authenticate using a managed identity:
Connect-MgGraph -Identity -NoWelcome
A managed identity is a system-managed highly secure identity. All the major Microsoft 365 PowerShell modules support system-assigned managed identities. Using a managed identity for authentication means that you don’t need to worry about passwords, secrets, or X.509 certifications.
After copying the code into the runbook and adding the connection via a managed identity, the runbook should look like Figure 4.

The test pane allows you to test the code under Azure Automation. When the test pane loads, click Start. Azure Automation goes through its process to allocate a server, provision the server with the necessary resources, and then run the code. When the code finishes, you’ll see the output (Figure 5). It’s always nice to see the expected result when an Azure automation runbook stops.

Lots More Possible with Azure Automation Runbooks
We’ve been through a basic example to explore the principles involved in creating an Azure Automation account, adding resources and permissions, and running some code. There’s lots more to do from this point: code will be more complex and probably create some output like email, SharePoint Online documents, or Teams messages, more resources and permissions will be needed, and you’ll probably want to explore how to schedule jobs so that they run on a regular basis. For instance, checking audit events weekly for signs of any problems with tenant security.
Azure Automation isn’t overly complex. Like all of us, it just needs to be appreciated in its own way.
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.