Azure Automation – Office 365 for IT Pros https://office365itpros.com Mastering Office 365 and Microsoft 365 Mon, 23 Jun 2025 21:46:58 +0000 en-US hourly 1 https://i0.wp.com/office365itpros.com/wp-content/uploads/2024/06/cropped-Office-365-for-IT-Pros-2025-Edition-500-px.jpg?fit=32%2C32&ssl=1 Azure Automation – Office 365 for IT Pros https://office365itpros.com 32 32 150103932 Microsoft 365 PowerShell Modules Need Better Testing https://office365itpros.com/2025/06/25/microsoft-365-powershell-azure/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-365-powershell-azure https://office365itpros.com/2025/06/25/microsoft-365-powershell-azure/#respond Wed, 25 Jun 2025 07:00:00 +0000 https://office365itpros.com/?p=69757

Problems with Azure Automation Afflict Microsoft 365 PowerShell Modules

The recent problems with the Microsoft Graph PowerShell SDK are well documented. Suffice to say that the Graph PowerShell SDK hasn’t been very stable since V2.25. V2.26 and V2.27 just didn’t work, and although Microsoft delivered a much-improved update in V2.28 in May 2025, the Graph PowerShell SDK still has problems with Azure Automation.

In the Azure Automation environment, runbooks are configured to use a runtime version of PowerShell. When a runbook starts, Azure Automation loads the dependent modules (which must be a version that matches the runtime version) on the target server where the runbook executes. Currently, Azure Automation supports runtime versions for PowerShell V5.1, V7.1, and V7.2.

A Question of .NET

PowerShell V5.1 is the “classic” version. V7-based PowerShell is “PowerShell Core.” The V7.1 and V7.2 runtimes support .NET 6 while the latest versions of PowerShell use .NET 8. Software engineering groups don’t like supporting what they consider to be outdated software, so a decision was taken to drop support for .NET 6. The net effect was that V7.1 and V7.2 runbooks couldn’t use the Graph PowerShell SDK. The workaround was to use the PowerShell V5.1 runtime or revert to V2.25 of the Graph PowerShell SDK, which still supports .NET6.

Microsoft says that the solution will come when Azure Automation supports the PowerShell V7.4 runtime. That update was supposed to arrive by June 15, 2025. It’s late, so I cannot confirm or deny if Graph PowerShell SDK V2.28 code supports PowerShell V7.4 runbooks.

The .NET Versioning Problem Strikes Exchange

A week or so ago, a reader complained that the latest version of the Exchange Online management module (now V3.8.0) didn’t run with PowerShell V7.2 runbooks. A previous comment for the article where the issue was raised said that V3.5 was required to support PowerShell V7.2 runbooks as long ago as February 13, 2025. At the time, apart from finding a relevant Stack Overflow discussion, I didn’t pay too much attention to the problem. I guess I became accustomed to the Exchange module just working while the Graph PowerShell SDK was the problem child of the Microsoft 365 PowerShell modules.

As it turns out, the Exchange Online management module shares the same problem as the Microsoft Graph PowerShell SDK. Engineering decided to remove support for .NET 6 in V3.5.1 of the Exchange module and screwed up Azure Automation V7 runbooks. The release notes for V3.5.1 are brief and concise:

Version 3.5.1

  • Bug fixes in Get-EXOMailboxPermission and Get-EXOMailbox.
  • The module has been upgraded to run on .NET 8, replacing the previous version based on .NET 6.
  • Enhancements in Add-VivaModuleFeaturePolicy.

There’s nothing to raise awareness for tenant administrators that the change in supported .NET version will stop runbooks dead in the water. It’s easy to glance over the release notes and conclude that not much has changed and it’s therefore safe to upgrade to the new version. The problem becomes very evident when the Connect-ExchangeOnline cmdlet can’t run and as a result, every other Exchange cmdlet cannot be found (Figure 1).

An Exchange Online management runbook barfs when run by Azure Automation.

Microsoft 365 PowerShell.
Figure 1: An Exchange Online management runbook barfs when run by Azure Automation

The Need for Solid Azure Automation Support

No one denies that Microsoft must prune old software from their cloud services. It’s hard enough to keep a service running smoothly when it carries unnecessary baggage in the form of old code. But in the cases of both the Microsoft Graph PowerShell SDK and the Exchange Online Management module, it seems like the engineering groups never stopped to ask if the change might impact the ability of scripts to run. Running scripts interactively revealed no issues, but running code in an interactive session on a Windows PC (or even a Mac) is not the same as Azure Automation firing up a headless Linux server and configuring it with the software necessary to execute a runbook.

Ensuring that shipped modules support Azure Automation is a problem that can be solved by incorporating Azure Automation runbooks in the test procedures that must succeed before a new version of a module can be released. What’s more upsetting is the lack of awareness within Microsoft about why customers pay for Azure Automation to run scripts.

When a script moves from running interactively on an administrator workstation to become an Azure Automation runbook, it’s probably because the script is deemed to be important enough to run on a stable, robust, and secure environment, often on a schedule (the Windows Task Schedule should not be relied upon to run important scripts). In other words, Azure Automation is an important platform that deserves the respect and solid support of the Microsoft engineers that build PowerShell modules that can run within Azure Automation. That doesn’t seem to be the case today.

Too Much Disruption

Microsoft 365 tenants have suffered far too much disruption with PowerShell modules over the last few years. The retirement of the old Azure AD and MSOL modules was a necessary evil, but Microsoft didn’t handle the situation as well as they should. Many sins might be forgiven if the Microsoft 365 PowerShell modules were rock solid. They’re not currently. Let’s hope that Microsoft does a better job in their testing and pre-release verification processes for PowerShell modules in the future.


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.

]]>
https://office365itpros.com/2025/06/25/microsoft-365-powershell-azure/feed/ 0 69757
Microsoft Attempts to Fix Microsoft Graph PowerShell SDK Problem with Azure Automation https://office365itpros.com/2025/04/14/microsoft-graph-powershell-sdk-2261/?utm_source=rss&utm_medium=rss&utm_campaign=microsoft-graph-powershell-sdk-2261 https://office365itpros.com/2025/04/14/microsoft-graph-powershell-sdk-2261/#respond Mon, 14 Apr 2025 07:00:00 +0000 https://office365itpros.com/?p=68847

.NET Dependencies Stop Microsoft Graph PowerShell SDK Authentication in Runbooks

As anyone who keeps tabs on the Microsoft Graph PowerShell SDK, the V2.26 release was a disaster. Poor testing and other failures let obvious problems escape into customer environments. To be fair to Microsoft, the development group fixed some of the more grievous problems and issued version 2.26.1 a week or so after V2.26 appeared.

Alas, V2.26.1 came with its own set of flaws, notably breaking Azure Automation runbooks that use PowerShell V7.1 and V7.2 because the SDK developers decided to remove support for .NET 6. Cue the infamous “invalid JWT access token” issue (Figure 1).

Authentication fails for V2.26.1 of the Microsoft Graph PowerShell SDK.
Figure 1: Authentication fails for V2.26.1 of the Microsoft Graph PowerShell SDK

All in all, the Microsoft Graph PowerShell SDK descended into grand farce, and no one knew what would happen next. The problem only happens for runbooks based on PowerShell V7.1 and V7.2. It doesn’t arise when runbooks use PowerShell V5.1.

Microsoft’s Solution – Azure Automation Support for PowerShell V7.4

On April 10, 2025, Microsoft laid out their plans to clean up the mess. Explaining that the root cause of the problem in V2.26.1 is a component conflict between the Exchange Online PowerShell module and the Microsoft Graph PowerShell SDK that prevents the Connect-MgGraph cmdlet working, Microsoft says that the issue is resolved when Azure Automation is upgraded to support PowerShell V7.4 (based on .NET 8). A preview of PowerShell V7.4 support is available today.

Microsoft doesn’t say when Azure Automation will fully support PowerShell V7.4 support in a generally available version.

Update 18 April: According to notes from a community call, Microsoft expects to release PowerShell 7.4 support for Azure Automation on June 15, 2025.

However, they do say that the next release of the Microsoft Graph PowerShell SDK is “expected later this month.

Update: Microsoft released V2.27 of the Microsoft Graph PowerShell SDK on April 19, 2025. The new version fixes some of the problems seen in V2.26.1. However, it still has problems with V7.1 and V7.2 Azure Automation runbooks.

Until Microsoft releases PowerShell V7.4 support for Azure Automation, if you have Azure Automation runbooks, stay with Microsoft Graph PowerShell SDK V2.25 or use V2.27 with PowerShell 5.1 runbooks.

Work Remains to be Done

Assuming that Microsoft delivers a new version of the Microsoft Graph PowerShell SDK that delivers “enhanced stability” (couldn’t be worse than the last two versions), “compatibility and performance” and address the many issues reported in the SDK GitHub repository (163 open at present), is that the end of this saga?

I don’t think so. The history of the Microsoft Graph PowerShell SDK is littered with poor quality and buggy releases. The clash with the Exchange Online PowerShell module speaks of a failure within Microsoft to coordinate updates to critical PowerShell modules used by Microsoft 365 customers. Given the closely-connected nature of Microsoft 365, it’s unacceptable for engineering groups to make changes to PowerShell modules without understanding if their updates will impact modules like Teams, SharePoint, and Exchange.

Quality instead of Fast-Paced Releases

Customers need a sustained run of high-quality Microsoft Graph PowerShell SDK releases to rebuild faith. In the past, Microsoft issued new SDK versions on a monthly cadence in an attempt to keep up with changes in Graph APIs. That cadence is too rapid. Stability should be the name of the game from here on with focus on delivering a high-quality quarterly SDK. Lessening the pace will permit the SDK engineers to coordinate better with their peers and burn down the swelling bug list. If people need to use a new Graph API, there’s no need to wait for Microsoft to build an SDK cmdlet because they can always use the API via the Invoke-MgGraphRequest cmdlet.

Over three million downloads now occur for new SDK versions. It’s time that Microsoft treats the Microsoft Graph PowerShell SDK as what it is: a serious piece of the PowerShell framework for Microsoft 365 automation.

]]>
https://office365itpros.com/2025/04/14/microsoft-graph-powershell-sdk-2261/feed/ 0 68847
Microsoft Graph PowerShell SDK V2.26.1 Remains Flawed https://office365itpros.com/2025/03/04/powershell-sdk-problems/?utm_source=rss&utm_medium=rss&utm_campaign=powershell-sdk-problems https://office365itpros.com/2025/03/04/powershell-sdk-problems/#comments Tue, 04 Mar 2025 07:00:00 +0000 https://office365itpros.com/?p=68280

Microsoft Graph PowerShell SDK Problems Means that Reputation Won’t be Easily Fixed

On February 25, 2025, I described how bugs in V2.26 of the Microsoft Graph PowerShell SDK made the software unusable. Late the same day, Microsoft pushed out V2.26.1. After a week’s testing, the signs are that the new version solved some of the problems seen in V2.26. However, bugs are still present in V2.26.1, including licensing failires and a nasty “invalid JWT access token” issue encountered when running the Connect-MgGraph cmdlet in an Azure Automation runbook. This error means that Connect-MgGraph failed to authenticate with Entra ID to secure an access token to allow the runbook to access data. It’s a pretty fundamental problem that’s accompanied by other issues reported on the Microsoft Graph PowerShell SDK GitHub issues page.

 The Connect-MgGraph cmdlet suffers from an invalid JWT access token problem.

Microsoft Graph PowerShell SDK problem.
Figure 1: The Connect-MgGraph cmdlet suffers from an invalid JWT access token problem

Given the current state, my advice is to stay on V2.25 until we know that a solid newer version of the Microsoft Graph PowerShell SDK is available.

On the surface, progress in squashing bugs is happening, and we can carry on using the SDK to generate automation solutions for Entra ID and Microsoft 365 as normal. Alas, that’s not a great plan At least, if Microsoft continues to develop the Microsoft Graph PowerShell SDK as before, exactly the same problems will appear in future versions. Lack of testing and poor communication will lead to more bugs and heightened customer dissatisfaction as Microsoft replays what happened with V2.14, V2.17/18, and now V2.26.

The Question of Testing to find Microsoft Graph PowerShell SDK Problems

Customers who experienced problems after installing V2.26 can justifiably ask what testing happens during the release cycle for a new version of the Microsoft Graph PowerShell SDK. I’m sure that some testing happens. The problem is that the testing has proven ineffective at picking up problems with heavily-used cmdlets like New-MgGroupMember and Send-MgUserMail, both of which were obviously flawed in V2.26.

It’s hard to test PowerShell cmdlets. Your use of a cmdlet might not be the way I use a cmdlet (the flexibility of PowerShell can be its own worst enemy at times). One method might be to test each cmdlet using the examples in the documentation. This sounds feasible, but it’s not. First, not all cmdlets have documented examples. Second, when examples exist, the code invariably reflects the simplest use of the cmdlet. For instance, the Send-MgUserMail cmdlet would have passed a test in V2.26 in terms of being able to send a simple message; its problems were revealed with moderately complex HTML body parts and attachments.

Third, there are just too many cmdlets to test. For V2.26.1, the number is 39,878 cmdlets (11,445 production (V1.0) and 28,433 beta). The SDK spans 38 sub-modules for the production cmdlets and 43 for beta cmdlets. These numbers grow over time as new Graph APIs appear.

Remember that the Microsoft Graph PowerShell SDK cmdlets are created by the Autorest process, which reads the Graph API schema and metadata to discover the resources and methods used to access data. The result is a cmdlet for every API. Some cmdlets or their documentation are flawed due to errors in the Graph metadata.

The challenge is to improve the quality of SDK cmdlets and documentation by making sure that the foundation (metadata) is right, and the cmdlets work the way that they should for every release. Developers don’t like surprises, and they especially don’t like when code that works with one version fails in another.

The Need for Crystal Clear Communication about Microsoft Graph PowerShell SDK Problems

The engineering group for the Microsoft Graph PowerShell SDK needs to improve its communication dramatically. Apart from some brief release notes (probably not read by many people), the bomb that lay within V2.26 was not discussed. The bomb was the impact on Azure Automation runbook due to the retirement of support for .NET6 and .NET7.

The release notes for V2.26.1 are equally terse. It’s possible to understand that the SDK rolled back to support .NET6 to address the Azure Automation issue, but not even the full changelog adds much value for anyone who’s not a professional developer, familiar with the SDK structure, and knows how to manage GitHub repositories.

The thing that seems to be forgotten is that many Microsoft 365 tenant administrators use the Microsoft Graph PowerShell SDK. Usage of the SDK is going up due to the imminent retirement of the MSOL and Azure AD modules.

V2.25 of the Microsoft Graph PowerShell SDK clocked up 3.6 million downloads compared to 1.18 million for V2.24. These are higher download numbers than for the Exchange Online management, Teams, SharePoint Online, or Entra modules and should be enough to prove the popularity of the SDK.

Tenant administrators are not developers. Most will have a passing knowledge of GitHub, and few will be able to trace the development of a new version through a GitHub changelog. What’s needed is clear, concise, and explicit explanations of what has changed in a new version, what impact this might have on existing code (if any), and any steps a tenant administrator must take to ensure that their code will continue to work.

It’s not too much to ask for people who create code that’s used by millions of people to communicate clearly with their audience. The temptation to use GitHub to generate release notes from change logs might be overpowering, but it’s simply not good enough. Context is everything, and bald statements that such and such a component was updated to fix bug number 3133 is never going to be an example of good communication.

Learning from Coping with Many Microsoft Graph PowerShell SDK Problems

I hope Microsoft learns from the V2.26 fiasco. It was a debacle created from their own making due to perfectly avoidable circumstances. To their credit, the SDK developers scrambled quickly to fix problems and get V2.26.1 out the door, but that’s still no reason for inflicting so much heartache on what should be their most fervent admirers.

There’s no doubt that the Microsoft Graph PowerShell SDK is a great tool for Microsoft 365 automation, like creating SharePoint pages from an RSS feed (just one example). Being able to interact with multiple workloads through a single SDK makes the pain somewhat bearable, except when it happens frequently. Trust in the PowerShell SDK was degraded by the V2.26 experience. I hope we see progress to allow SDK fans to build that trust back again.


Need some assistance to write great 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.

]]>
https://office365itpros.com/2025/03/04/powershell-sdk-problems/feed/ 2 68280
Microsoft Graph PowerShell SDK Runs into Choppy Waters https://office365itpros.com/2025/02/25/graph-sdk-v2-26-issues/?utm_source=rss&utm_medium=rss&utm_campaign=graph-sdk-v2-26-issues https://office365itpros.com/2025/02/25/graph-sdk-v2-26-issues/#comments Tue, 25 Feb 2025 07:00:00 +0000 https://office365itpros.com/?p=68202

Graph SDK V2.26 Issues Make the Software Unusable

The Microsoft Graph PowerShell SDK is becoming increasingly popular. With over 3.5 million downloads of the previous version, a new release was bound to be a major event., especially after a three-month delay since Microsoft released V2.25 on November 21, 2024 (the SDK usually follows a monthly release cadence). V2.26 duly arrived five days ago.

Alas, you should avoid and not install V2.26. The release is buggy, exhibits little evidence of being tested before launch, and creates huge operational problems for Azure Automation-based runbooks.

The Cracks Appear

I installed V2.26 soon after it appeared in the PowerShell Gallery. I have a large number of PowerShell scripts and runbooks based on the Microsoft Graph PowerShell SDK and it didn’t take long before cracks appeared. For example, HTML messages generated by the Send-MgUserMail cmdlet didn’t display property in multiple email clients, including Outlook classic, the new Outlook, OWA, and Proton (Figure 1).

Odd encoding for HTML message body parts.

SDK V2.26 Bugs
Figure 1: Odd encoding for HTML message body parts

Another issue was that the Send-MgUserMail cmdlet failed to process attachments. The script I published last week to show how to add and send multiple attachments with Outlook messages failed spectacularly. Many production scripts use Send-MgUserMail to generate and send HTML formatted messages, so this issue was a big problem for a new release.

To be fair to Microsoft, they quickly fixed the two Send-MgUserMail issues. However, these weren’t the only problems. For instance, the New-MgGroupMember cmdlet failed because of an “invalid URL format” generated by an odd value appended to the request URI created for the POST request to add a new group member.

As is my norm, I reported the issues as I encountered problems (anyone who finds a problem with the Microsoft Graph PowerShell SDK, can report the issue online). After a couple of hours, it was evident that V2.26 was in bad shape and practically unusable.

The Big Issue

Discovering several Priority 1 issues in a short period is bad enough; finding that V2.26 had dropped support for .NET6 and .NET7 without warning delivered the coup de grace. This news is a bullet point in the Microsoft Graph PowerShell SDK release notes, which is nice if you read that information and understand the full consequences of the removal. Microsoft delivered no context for the change and no commentary as to what might be the effect.

Issue #3151 was the first report that came in to advise that Azure Automation runbooks had stopped working with V2.26. As you might recall, before Microsoft Graph PowerShell SDK cmdlets can be used with Azure Automation, the modules containing the cmdlets must be loaded as resources into the automation account. You can then create runbooks based on a specific PowerShell version and use the cmdlets in those runbooks.

Azure Automation supports runbooks based on PowerShell V5.1, V7.1. and V7.2. Sometimes a module only supports V5.1 (the SharePoint Online management module is an example), but usually it’s recommended to use PowerShell 7. The problem introduced with the new version of the SDK is that PowerShell V7.1 and V7.2 don’t use .NET 8. Because the SDK dropped support for .NET6 (which reached end of support in November 2024) and .NET7 (which reached end of support in May 2024), Azure Automation and the Microsoft Graph PowerShell SDK are now at odds with each other.

To demonstrate, I created a new automation account and loaded the Microsoft.Graph.Authentication module into the account (Figure 2). This module is the foundation of the Microsoft Graph PowerShell SDK because all other modules have a dependency on it. Note that the runtime version is PowerShell 7.2.

Modules loaded in an Azure Automation account include the Microsoft.Graph.Authentication module.
Figure 2: Modules loaded in an Azure Automation account include the Microsoft.Graph.Authentication module

Next, I created a very simple runbook to sign into the Graph using a managed identity (Figure 3). The only processing performed by the runbook is to sign in and report the authentication context.

A very simple PowerShell runbook using the Microsoft Graph PowerShell SDK.
Figure 3: A very simple PowerShell runbook using the Microsoft Graph PowerShell SDK

When I ran the runbook, it failed because the runbook couldn’t load an assembly it needed to run the Connect-MgGraph cmdlet. This isn’t surprising because PowerShell 7.2 is looking for a file that it doesn’t support or isn’t available.

The runbook could not load a file or assembly.
Figure 4: The runbook could not load a file or assembly

Stay with V2.25

You won’t have a problem if automation accounts keep on using V2.25. You will have problems if you upgrade automation accounts to use the V2.26 modules or create new automation accounts and load the V2.26 modules (which is what Azure Automation offers) as account resources. Some workarounds have been suggested, such as using the Azure Runtime Environment, but that’s a preview feature and I don’t recommend using it in production. You’re safer by leaving the V2.25 modules in place and not attempting to upgrade.

A Big Mess

It’s obvious that V2.26 of the Microsoft Graph PowerShell SDK is a big mess. It’s not the only fiasco in SDK history as previous problems were experienced in V2.14 and V2.17/V2.18 in 2024. It’s obvious that Microsoft didn’t test this release thoroughly before pushing it out the door. It’s also clear that Microsoft failed to appreciate or communicate the impact of removing support for .NET6 and .NET7 on Azure Automation. I don’t recall seeing a support article that clearly outlines how V2.26 affects Azure Automation that might convince customers to pause upgrading to V2.26.

Although the two .NET releases are retired, PowerShell in Azure Automation hasn’t moved on to support PowerShell V7.5. Perhaps that’s because the Azure Automation development team know that dropping support for PowerShell V7.1 and V7.2 might break many production runbooks. Still, that’s an internal issue for Microsoft to work out between the development groups. Customers shouldn’t have to experience the downside of the lack of coordination and planning that obviously exists within the world’s largest software company.

If you’re one of the 150,000 who downloaded V2.26 SDK, it’s time to bin it and revert to V2.25. At least that version works.

Update February 26: Microsoft has released V2.26.1 of the Microsoft Graph PowerShell SDK to fix some of the known bugs found in V2.26. I would not be in a rush to download and install this release until the situation around bugs and fixes clarifies.

Update: V2.26.1 has proven unreliable. Do not upgrade to this release.


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.

]]>
https://office365itpros.com/2025/02/25/graph-sdk-v2-26-issues/feed/ 7 68202
Primer: Using Exchange Online PowerShell in Azure Automation Runbooks https://office365itpros.com/2025/02/10/azure-automation-exchange-primer/?utm_source=rss&utm_medium=rss&utm_campaign=azure-automation-exchange-primer https://office365itpros.com/2025/02/10/azure-automation-exchange-primer/#comments Mon, 10 Feb 2025 07:00:00 +0000 https://office365itpros.com/?p=67992

Exchange Online PowerShell Assumes Administrators Run Its Cmdlets

My last primer article in the Azure Automation series covered how to send email using the Exchange Online High-Volume Email (HVE) facility. HVE is still in preview (Microsoft is targeting September 2025 for general availability) but it still does a nice job of sending email from scheduled automation jobs.

This article discusses how to create and execute Azure Automation Exchange runbooks using PowerShell cmdlets from the Exchange Online management module. Unlike HVE, which doesn’t require any Exchange cmdlets, Automation accounts that use the Exchange module in their jobs need some special configuration. This is because the Exchange module assumes that anyone running its cmdlets is an Exchange administrator. There’s no concept of least privilege implemented in the module: once a process loads the module, it can act like a human administrator.

Loading Exchange Online PowerShell into an Automation Account

At least, an app can be all-powerful for Exchange if it meets three conditions. First, it can load the Exchange Online management module. For Azure automation accounts, this means that module is loaded as a resource into the account (Figure 1).

Selecting the Exchange Online management module to load into an Azure Automation account.
Figure 1: Selecting the Exchange Online management module to load into an Azure Automation account

At the time of writing, Exchange Online PowerShell only supports PowerShell V5.1 for automation runbooks, so be sure to install that version of the module. Due to module dependencies, you must install the PackageManagement and PowerShellGet modules (loaded jn that order) before installing the Exchange Online module.

Assigning Exchange Online Permissions and Roles for the Automation Account

Second, the service principal for the app must be assigned the Exchange administrator RBAC role. For Azure Automation, this means the service principal for the automation account. The assignment can be done through the Entra admin center (Figure 2) or with PowerShell. Make sure that you select the correct automation account from the set of enterprise applications listed in the picker.

Selecting the service principal for an automation account to assign the Exchange administrator role.
Figure 2: Selecting the service principal for an automation account to assign the Exchange administrator role

Third, the app must be assigned the Exchange.ManageAsApp permission. This is not a Microsoft Graph permission. It is an Office 365 Exchange Online permission designed to allow apps to act as administrators. The assignment can only be made through PowerShell. Here’s how to do the job with the Microsoft Graph PowerShell SDK:

$ExoApp = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
$TargetSP = Get-MgServicePrincipal -filter "displayname eq 'M365Automation'"
$Role = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}
$AppRoleAssignment = @{}
$AppRoleAssignment.Add("PrincipalId",$TargetSP.Id)
$AppRoleAssignment.Add("ResourceId",$ExoApp.Id)
$AppRoleAssignment.Add("AppRoleId",$Role.Id)

$RoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $TargetSP.Id -BodyParameter $AppRoleAssignment
If ($RoleAssignment.AppRoleId) {
  Write-Host ("{0} permission granted to {1}" -f $Role.Value, $TargetSP.DisplayName)
}

Creating a Runbook to use Exchange Online Cmdlets

With the three prerequisites in place, you can create a runbook. To test that everything works as expected, create a V5.1 PowerShell runbook with the following code (replace the organization name with your tenant):

Connect-ExchangeOnline -ManagedIdentity -Organization Office365itpros.com
(Get-OrganizationConfig).DisplayName

Save the runbook and use the test pane to execute it. The output should be the display name for your organization. If that’s all you see, you can go ahead and build out the runbook with code to do more useful work.

As a demonstration, I took the script to report missing properties for user mailboxes and copied it into the runbook. The only changes that I made were:

  • Remove the code that checks for an active connection to Exchange Online at the start of the script and replace it with the Connect-ExchangeOnline -ManagedIdentity command.
  • Remove the Clear-Host cmdlet (Azure Automation doesn’t have a host to clear).
  • Replace the Write-Host cmdlet with Write-Output (Azure Automation outputs everything together (a stream) at the end of a job).
  • Remove the code to output the results as an CSV file at the end of the script.

Figure 3 shows the output of the runbook in the test pane. Everything works and we know that there are some mailboxes with missing properties that should be addressed.

Output from an Exchange Online script run by Azure Automation.

Azure automation Exchange Online.
Figure 3: Output from an Exchange Online script run by Azure Automation

Azure Automation can create an output file on the headless server where the runbook executes, but the question is then how to copy the file to somewhere more accessible later. The easy answer is to use HVE to send the file as an email attachment or to include the data in the body of a message. Something more complicated, like creating a file in a SharePoint Online site, will require more effort.

Not So Difficult

Running Exchange Online scripts in Azure Automation isn’t difficult once the initial setup for the automation account is in place. Some tweaking of the script code is probably necessary, but it’s not difficult to make the changes and will become second nature after a while. If you need to run jobs that process large numbers of Exchange objects (like mailboxes), Azure Automation is an excellent platform choice.


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.

]]>
https://office365itpros.com/2025/02/10/azure-automation-exchange-primer/feed/ 21 67992
Primer: Using Exchange High Volume Email with Azure Automation https://office365itpros.com/2025/01/29/use-hve-with-azure-automation/?utm_source=rss&utm_medium=rss&utm_campaign=use-hve-with-azure-automation https://office365itpros.com/2025/01/29/use-hve-with-azure-automation/#respond Wed, 29 Jan 2025 07:00:00 +0000 https://office365itpros.com/?p=67863

Use HVE with Azure Automation Runbooks to Send Email

The last article in the series about using Azure Automation with Microsoft 365 showed how to send email with the Send-MgUserMail cmdlet. The cmdlet can create and send messages from a designated user or shared mailbox to any valid SMTP recipient. As explained in the article, the approach works well for communicating details of background processing, such as reviewing audit records for suspicious activity, especially when run as a scheduled job. Computer systems are better than humans at remembering when they have work to do.

HVE and ECS Email Options

An issue raised in the Office 365 for IT Pros GitHub repository about a script I wrote for an article about the Exchange Online High Volume Email (HVE) feature prompted me to think about using HVE in an Azure Automation runbook.

HVE processes email often created by line of business applications or messages intended for high-volume circulation, such as corporate updates or marketing communications. The focus for HVE is internal email. Although HVE can send mail externally, Microsoft restricts external recipients to 2,000 daily per HVE account. The Azure Email Communication Service is a better solution for external email.

The HVE Scenario and Credentials

Let’s put together a scenario to test how to use HVE with Azure Automation. I decided to check a fact published on a website and act depending on the website content. As you might know, we publish monthly updates for the Office 365 for IT Pros eBook. The date of the most recent update and its release both appear on the book’s page (Figure 1).

The last published date for the Office 365 for IT Pros eBook is available in a webpage.
Figure 1: The last published date for the Office 365 for IT Pros eBook is available in a webpage

The runbook uses the Invoke-WebRequest cmdlet to fetch the webpage content. It then parses the page to find the latest published release date. For test purposes, the runbook compares the publication date against a hardcoded date and sends email to a distribution list if it detects a new publication date. In a production environment, the date of the last known update could be stored and updated in a repository like a SharePoint Online list.

HVE supports basic and OAuth authentication. Microsoft plans to remove support for basic authentication for the SMTP AUTH protocol in September 2025. HVE uses a different SMTP endpoint and its traffic is limited to clients that can connect using a HVE account, so it’s unaffected by the deprecation in September. For now, you can pass the username and password for a HVE account to authenticate. When Microsoft removes this capability, the code will need to be updated to fetch an access token to use to authenticate.

I use an Azure Key Vault in the Azure account used for Azure Automation to store the HVE account credentials. Storing information in Key Vault eliminates any need for hardcoded credentials, which is always good.

Outline of the Script to use HVE with Azure Automation

The good news is that an automation account doesn’t need any additional resources to be loaded or consent for any special permissions before it can send email via HVE. The cmdlets necessary to interact with Azure Key Vault are in the AZ modules that Azure Automation automatically maintains for automation accounts (you don’t need to install or update these modules in the same way as you need to manage Microsoft 365 modules). The same is true for the Import-WebRequest and Send-MailMessage cmdlets. Send-MailMessage is now an old cmdlet that Microsoft would like to deprecate, but it works to send HVE messages.

The script goes through these steps.

  • Uses a managed identity to connect to Azure.
  • Fetches the user credentials for the HVE account from Azure Key Vault and builds a credentials object.
  • Retrieves the content of the target web page and checks it to find the last update for the book.
  • If an update is found, constructs a message and sends it to the distribution group via HVE (Figure 2).
A message sent by an Azure Automation runbook using HVE.

Use HVE with Azure Automation.
Figure 2: A message sent by an Azure Automation runbook using HVE

You can download the complete runbook (script) from the Office 365 for IT Pros GitHub repository.

It was surprisingly easy to put together this proof of concept and demonstrate how to use HVE with Azure Automation. It’s a good example of how a scheduled background process can check if something has changed and take action when necessary. The hardest part was figuring out how to extra the publication date from the website. Once that was done, the code came together quickly because I was able to “borrow” bits from scripts written for previous articles.


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.

]]>
https://office365itpros.com/2025/01/29/use-hve-with-azure-automation/feed/ 0 67863
Primer: Running Audit Searches and Sending Email from Azure Automation https://office365itpros.com/2025/01/28/azure-automation-for-audit-searches/?utm_source=rss&utm_medium=rss&utm_campaign=azure-automation-for-audit-searches https://office365itpros.com/2025/01/28/azure-automation-for-audit-searches/#respond Tue, 28 Jan 2025 07:00:00 +0000 https://office365itpros.com/?p=67840

Use Azure Automation for Audit Searches

In the past, I published articles covering the basics of using Azure Automation to process Microsoft 365 data. The articles cover the basics of using an automation account to execute runbooks (PowerShell scripts based on the Microsoft Graph PowerShell SDK), how to output results to a SharePoint Online list, and how to attach runbooks to automation schedules to make sure that processes execute automatically and reliably.

This article covers how to execute Microsoft 365 audit searches in runbooks and how to send the results extracted from the audit searches via email. I’m going to use the scenario discussed on 24 January about a flaw found in Entra ID that allowed users to change their user principal names. Microsoft has since addressed the problem, but the fact still remains that changes to user principal names can have consequences for services other than authentication. Any change like this deserves oversight. The purpose is to explore the principles rather than the details of a solution, and the techniques used here can be applied to any audit log search.

Basic Outline to Create a Runbook for a Microsoft 365 Audit Search

Two methods are available to search the unified audit log.

Opting for the Graph API makes sense for an Azure Automation job. Asynchronous searches take longer but that doesn’t matter when the job executes in the background, especially if it’s a scheduled run. In terms of the code, Microsoft has temporarily withdrawn the Get-MgBetaSecurityAuditLogQuery cmdlet and the Get-MgBetaSecurityAuditLogQueryRecord sometimes doesn’t work, so we use Graph API requests in this example. I used V2.25 of the Graph SDK and the cmdlets might have returned by the time you read this text.

The basic processing steps are:

  • Construct the parameters for the audit log search. The Update User operation captures changes made to user accounts, so that’s what the search looks for over the last seven days.
  • Submit the search and monitor its progress until completion.
  • Retrieve the audit records.
  • Process the audit records to check if any are for changes to the userPrincipalName property and capture details of these events.
  • Create a HTML fragment containing the events and use it to create the HTML content for a message.
  • Run the Send-MgUserMail cmdlet to send the message to a predetermined recipient. This can be any valid email address. In production, it’s likely that the recipient would be a distribution list, but it could be a Microsoft 365 group, or even a Teams channel.

Testing the Runbook for a Microsoft 365 Audit Search

As always, it’s wise to test the runbook code by running it interactively in a Microsoft Graph PowerShell SDK session. The automation account must have the AuditLogsQuery.Read.All application permission to access audit logs and Mail.Send to be able to send email. See my earlier post for how to assign Graph permissions to automation accounts. In production scenarios, you should use RBAC for Applications to restrict access for the automation account to specific mailboxes.

To mimic what happens when Azure Automation executes the runbook, use an app-only session by signing in with an app identifier, tenant identifier, and a certificate uploaded to the app. The app must have consent to use the two permissions listed above. Once everything works interactively, copy the code and create a new Azure Automation runbook and test that the code runs in that environment (Figure 1).

Testing a runbook to extract audit events in Azure Automation.

Azure automation for audit searches.
Figure 1: Testing a runbook to extract audit events in Azure Automation

When everything checks out, you can register the runbook with an automation schedule. This check is a good example of something that should be done bi-weekly. Of course, what the recipients do when they receive the message (Figure 2) is up to them.

Details of audit events distributed by email sent by an Azure Automation runbook.
Figure 2: Details of audit events distributed by email sent by an Azure Automation runbook

Better than Microsoft 365 Audit Policies?

Similar functionality in terms of sending email notifications for events found by Microsoft 365 audit searches available through Microsoft 365 audit policies. The reason why a DIY version might be preferable is that you have full control over the content presented in messages and the advice given to recipients, plus any associated processing you might want to do. For instance, you could log the highlighted audit events in a SharePoint Online list and require administrators to attest that they checked each event to make sure that it’s appropriate. That might be too much, but it’s possible.

The code I used for testing can be downloaded from the Office 365 for IT Pros GitHub repository.


Insight like this doesn’t come easily. You’ve got to know the technology and understand how to look behind the scenes. Benefit from the knowledge and experience of the Office 365 for IT Pros team by subscribing to the best eBook covering Office 365 and the wider Microsoft 365 ecosystem.

]]>
https://office365itpros.com/2025/01/28/azure-automation-for-audit-searches/feed/ 0 67840
Primer: Output Data Generated with an Azure Automation Runbook to a SharePoint List https://office365itpros.com/2025/01/22/azure-automation-runbook-list/?utm_source=rss&utm_medium=rss&utm_campaign=azure-automation-runbook-list https://office365itpros.com/2025/01/22/azure-automation-runbook-list/#comments Wed, 22 Jan 2025 07:00:00 +0000 https://office365itpros.com/?p=67771

Execute an Azure Automation Runbook and Store its Results in a SharePoint Online List Item

Yesterday, I explained the basics of how to use Azure Automation to run a script using Microsoft Graph PowerShell SDK cmdlets. Today, I want to extend the knowledge outlined in that article to demonstrate another important aspect: How to output information from an Azure Automation runbook.

Azure Automation runbooks execute on headless servers that you don’t control. Runbooks can create output data but need to get that information back to whoever needs it. Available Microsoft 365 methods to share information include:

  • Creating a file in a SharePoint Online document library.
  • Posting a message to a Teams chat or channel.
  • Sending email.
  • Creating items in a list in a SharePoint Online site.

This article covers how to use the last method because SharePoint lists are a good way to capture the information generated by background processes. The script used yesterday reports user accounts created in the last 30 days. We’ll extend it to find some additional information and create a list item containing the data.

The Basics – Resources and Permissions

The first version of the script uses two modules of the Microsoft Graph PowerShell SDK for authentication and to find user accounts. To interact with SharePoint sites, we must add the Microsoft.Graph.Sites module and because the script generates some information about Microsoft 365 Groups, add the Microsoft.Graph.Groups module too.

The automation account already has the User.Read.All Graph permission. To read details of groups, it needs the Group.Read.All permission. The interaction with sites is both read (to access the site and find the target list) and write (to create items in the target list), so the automation account needs the Sites.ReadWrite.All permission. We’ll add the two permissions using PowerShell as follows:

Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All
# Add Graph permissions to 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'"

[array]$Permissions = "Group.Read.All", "Sites.ReadWrite.All"

ForEach ($Permission in $Permissions){
    $Role = $GraphApp.AppRoles | Where-Object {$_.Value -eq $Permission}

    # Create the parameters for the new assignment
    $AppRoleAssignment = @{}
    $AppRoleAssignment.Add("PrincipalId",$TargetSP.Id)
    $AppRoleAssignment.Add("ResourceId",$GraphApp.Id)
    $AppRoleAssignment.Add("AppRoleId",$Role.Id)

    $RoleAssignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $TargetSP.Id -BodyParameter $AppRoleAssignment
    If ($RoleAssignment.AppRoleId) {
        Write-Host ("{0} permission granted to {1}" -f $Role.Value, $TargetSP.DisplayName)
    }
}

After these commands execute, Figure 1 shows what you should see when viewing the permissions for the automation account in the enterprise apps section of the Entra admin center.

 Graph permissions needed to interact with users, groups, and sites.
Figure 1: Graph permissions needed to interact with users, groups, and sites

Prepare the Target List

Although you can create a list in a SharePoint Online site with PowerShell (here’s how), it’s easier to do this work through the SharePoint browser interface. Select a target site and create a list there. I used a minimal set of fields to capture details like the number of user accounts, the number of Microsoft 365 groups, the names of recently added user accounts, and a timestamp. Don’t get too worried about what data is output: we’re just exploring principles here instead of creating a fully-fledged solution. Remember to note the name of the fields you add to the list because they’ll need to be stated in the script (field names are case sensitive).

Amend the Runbook Code

Yesterday’s script is simple and spans just a few commands to find and list recently added user accounts. Today’s script needs more code. Let’s investigate.

First, we need to connect to the SharePoint site that holds the target list. This code takes a URL for a site and converts it to a SharePoint site identifier that can be used to find a site. We can then look for the target list and fetch its details.

$Uri = "https://office365itpros.sharepoint.com/sites/Office365Adoption"
$SiteId = $Uri.Split('//')[1].split("/")[0] + ":/sites/" + $Uri.Split('//')[1].split("/")[2]
$Site = Get-MgSite -SiteId $SiteId
If (!$Site) {
    Write-Output ("Unable to connect to site {0} with id {1}" -f $Uri, $SiteId) 
    Exit
}
$List = Get-MgSiteList -SiteId $Site.Id -Filter "displayName eq 'Tenant Statistics'"
If (!$List) {
    Write-Output ("Unable to find list 'Tenant Statistics' in site {0}" -f $Site.DisplayName)
    Exit
}

The script includes some simple code to find user accounts and Microsoft 365 groups:

[array]$UserAccounts = Get-MgUser -All -PageSize 500 -Filter "userType eq 'Member'"
[array]$M365Groups = Get-MgGroup -Filter "groupTypes/any(c:c eq 'unified')" -All -PageSize 500

Finally, the runbook has the code to create an item in the target list. This is accomplished by creating a hash table to hold details of the fields (inside a separate hash table). What seems to be an odd structure is because PowerShell is mimicking a JSON structure for a payload body submitted to the Graph request to add the item. In any case, here’s the code:

$NewItemParameters = @{
   fields = @{
     Title               = Get-Date ($Date) -format s
     Rundate             = $RunDate
     NumberM365Groups    = $M365Groups.Count
     NumberUserAccounts  = $UserAccounts.Count
     RecentUserAccounts  = $RecentUserAccounts
   }
}

$NewItem = New-MgSiteListItem -SiteId $Site.Id -ListId $List.Id -BodyParameter $NewItemParameters
If ($NewItem) {
    Write-Output ("Added item to list {0}" -f $List.DisplayName)
} Else {
    Write-Output "Failed to add item to list"

I can’t emphasize too much the importance of testing code interactively before submitting it to Azure Automation. When that happens, after running in test mode, the runbook should report that it created a new item in the list (Figure 2).

Testing that the Azure Automation runbook works and creates an item in the target SharePoint list.
Figure 2: Testing that the Azure Automation runbook works and creates an item in the target SharePoint list

To verify that the runbook succeeded, go to the SharePoint site and open the target list. The item(s) created by the runbook should be present.

Items created by the runbook in a SharePoint Online list.
Figure 3: Items created by the Azure Automation runbook in a SharePoint Online list

Running Azure Automation Runbooks Aren’t So Hard After All

I hope that by now you’ll understand that running PowerShell scripts with Azure Automation is not particularly difficult. Once a runbook can output data, that data can be processed further. Lists are particularly adaptable in this way because there are many ways to reuse the data through channels like Power Apps or Power BI.

There are many code examples available that can help to solve automation problems. But the most important thing is to get the basics right. When that happens, everything clicks into place.

The script I used for the runbook can be downloaded from the Office 365 IT Pros repository on GitHub.


Support the work of the Office 365 for IT Pros team by subscribing to the Office 365 for IT Pros eBook. Your support pays for the time we need to track, analyze, and document the changing world of Microsoft 365 and Office 365.

]]>
https://office365itpros.com/2025/01/22/azure-automation-runbook-list/feed/ 2 67771
Microsoft Retires Azure Automation Run As Accounts in September 2023 https://office365itpros.com/2023/02/06/run-as-account-retirement/?utm_source=rss&utm_medium=rss&utm_campaign=run-as-account-retirement https://office365itpros.com/2023/02/06/run-as-account-retirement/#respond Mon, 06 Feb 2023 01:00:00 +0000 https://office365itpros.com/?p=58977

Azure Automation for IT Pros

I’ve spent a lot of time working with Azure Automation over the last few years. It’s an extremely useful facility for tenant administrators who want to run PowerShell scripts using a more modern mechanism than offered by Windows Scheduler. This is especially true so in large tenants where processing hundreds or thousands of objects is common, which is why I started to use Run As accounts with Azure Automation.

Converting scripts to run on Azure Automation isn’t too difficult, once you understand the headless nature of the beast and that PowerShell runs on virtual machines spun up for the purpose. The biggest issue often faced when moving scripts from running interactively to being an Azure Automation runbook is how to create output from scripts, but it’s possible to send email, post to Teams channels, and create files in SharePoint document libraries.

Microsoft seems to communicate with developers and administrators (aka IT Pros) in different ways. For instance, the news about the retirement of Azure Automation Run As accounts on September 30, 2023, didn’t appear in any notification in the Microsoft 365 admin center. In fact, apart from the notices posted in Azure Automation documentation (like that shown in Figure 1), I can’t find a formal announcement from Microsoft.

Microsoft notice about the retirement of Run As accounts
Figure 1: Microsoft notice about the retirement of Run As accounts

Informing the Technical Community About the Run As Retirement

The possibility exists that I might not be looking hard enough. Normally, I am reasonably proficient with search (Google), but the first hit I find is a 27 September 2022 Microsoft Answers post saying “On 30 September 2023, we’ll retire the Azure Automation Run As account that you use for Runbook authentication.” I can find an earlier “plan for change” note for July 2022 in the What’s new in Azure Automation page. Apart from that, Microsoft seems to have updated the documentation on 18 October 2022 (here’s the FAQ).

I suppose that it’s reasonable to expect people to learn about developments from documentation. In this instance, I think Microsoft dropped the ball and didn’t do a great job of telling people what’s going to happen when Run As accounts retire.

Managed Identities Are a Better Solution

The logic for retiring Run As accounts is undeniable. A better and more secure solution (managed identities) exists. Run As accounts authenticate using a self-signed certificate that needs to be renewed yearly. Microsoft has removed the ability to renew these certificates from the Azure portal, meaning that Run As accounts are counting down to a time when they won’t be able to authenticate. Microsoft has a script to renew certificates for Run As accounts and the script will run after September 30, 2023. However, Run As accounts will then be unsupported, which isn’t a great situation for production components.

The nice thing about managed identities from an Office 365 perspective is that the important PowerShell modules used for automation support managed identities. Some do so very smoothly (like the latest Exchange Online management module, where even the latest RBAC for applications feature supports managed identities) and some do it with a little extra work. For example, V1.0 of the Microsoft Graph PowerShell SDK needs to get an access token from the Azure Automation account that owns a managed identity while V2.0 will be able to sign in using a managed identity. Here’s an example of a simple runbook that:

  • Connects to the Azure Automation account using a managed identity.
  • Gets an access token from Azure AD.
  • Uses the access token to connect to the Graph with Connect-MgGraph.
  • Retrieves the service domain (like office365itpros.onmicrosoft.com) using the Get-MgOrganization cmdlet.
  • Uses the service domain and a managed identity to connect to Exchange Online.
  • Lists details of user mailboxes.
# Connect to Microsoft Graph with Azure Automation
Connect-AzAccount -Identity
$AccessToken = Get-AzAccessToken -ResourceUrl "https://graph.microsoft.com"
Connect-MgGraph -AccessToken $AccessToken.Token
# Get Tenant service domain using Get-MgOrganization
$TenantName = (Get-MgOrganization).VerifiedDomains | Where-Object {$_.IsInitial -eq $True} | Select-Object -ExpandProperty Name
# Connect to Exchange Online
Connect-ExchangeOnline -ManagedIdentity -Organization $TenantName 
Get-ExoMailbox -RecipientTypeDetails UserMailbox | Format-Table DisplayName, UserPrincipalName

When V2.0 of the Microsoft Graph PowerShell SDK is available, you’ll be able to replace the first three lines of code with a simple Connect-MgGraph -Identity.

Another example of using a managed identity with Exchange Online is to monitor events gathered in the audit log to detect and report events that might indicate potential tenant compromise. Running the script on an Azure Automation schedule makes sure that audit events are checked without human intervention.

Time to Move Forward

Apart from the poor communication, I don’t have any problem with Microsoft’s decision to retire Run As accounts. They worked as a mechanism to connect resources to Azure Automation. We’re just moving on to adopt a new approach. Microsoft documents the migration steps to move from a Run As account to use managed identities. It’s a manual process, but not onerous.

]]>
https://office365itpros.com/2023/02/06/run-as-account-retirement/feed/ 0 58977
Upgrade of Teams Policy Cmdlets Enables Use in Azure Automation https://office365itpros.com/2022/11/03/teams-policy-assignment-azureauto/?utm_source=rss&utm_medium=rss&utm_campaign=teams-policy-assignment-azureauto https://office365itpros.com/2022/11/03/teams-policy-assignment-azureauto/#comments Thu, 03 Nov 2022 01:00:00 +0000 https://office365itpros.com/?p=57735

Now Possible to Make Teams Policy Assignments in Azure Automation Runbook

Microsoft released a new version (4.9) of the MicrosoftTeams PowerShell module on November 1. One of the changes made in this module is the upgrade of four policy management and assignment cmdlet sets so that they no longer have a dependency on the WinRM component. The policies include the Teams meeting and messaging policies, two of those most important and heavily used. The other two are the Teams feedback policy and the Teams voicemail policy.

Although many of the Teams PowerShell cmdlets are Graph-based, the Teams developers have been modernizing the older policy management cmdlets inherited from the Skype for Business Online connector. When modernized, cmdlets can run in environments like Azure Automation (this article explains the concept of using Teams PowerShell in Azure Automation). If you want to use Azure Automation with Teams, make sure that the service principals used for authentication have the necessary permissions.

Checking Teams Policy Assignments

Obviously, it’s a good thing for the Teams PowerShell cmdlets to support Azure Automation, but would you use these cmdlets in Azure Automation runbooks? I don’t think that you’d use Azure Automation for something like a bulk assignment of a Teams policy to user accounts. Teams already has its own bulk assignment mechanism.

The new capability is more likely to be used in periodic checks run against accounts to make sure that they have the right policy assignments. Given the number of Teams policies, it can be easy to miss out an inappropriate or erroneous assignment, so having a regularly-scheduled job to make sure that the right assignments are in place is a good idea. With the advent of the Teams Premium license, it might also be a way to make sure that these expensive add-ons ($10/user/month) are granted to the right accounts.

Example – Making Teams Policy Assignments for IT Department Members

To take a simple example, let’s assume that we want to assign specific meeting and messaging policies to members of the IT Department. The concept for the runbook is simple:

  • Find the users to process.
  • Grant the policies.

The Get-CsOnlineUser cmdlet from the Teams module hasn’t yet been modernized so it cannot be used with Azure Automation. Instead, we can use cmdlets from the Microsoft Graph PowerShell SDK or Exchange Online management modules to find target accounts. As I want to filter users based on department, I chose to use the Get-Recipient cmdlet.

The Grant-CsTeamsMeetingPolicy and Grant-CsTeamsMessagingPolicy are the two cmdlets needed to assign policies to the target accounts. Unhappily, I discovered that although the cmdlets work in an Azure Automation runbook, they don’t work when using a managed identity for authentication. Plan B duly ensued, and I reverted to fetching credentials for an account used for background processing from Azure Key Vault and used those credentials to connect to Teams instead. I guess you can’t expect perfection overnight.

Here’s the complete code:

# Connect to AzAccount to access Key Vault to fetch variables used by the script
$AzConnection = Connect-AzAccount -Identity | Out-Null
# Get username and password from Key Vault
$UserName = Get-AzKeyVaultSecret -VaultName "xxxxxx" -Name "AccountName" -AsPlainText
$UserPassword = Get-AzKeyVaultSecret -VaultName "xxxxxx" -name "AccountPassword" -AsPlainText
# Create credentials object from the username and password
[securestring]$SecurePassword = ConvertTo-SecureString $UserPassword -AsPlainText -Force
[pscredential]$UserCredentials = New-Object System.Management.Automation.PSCredential ($UserName, $SecurePassword)
Connect-MicrosoftTeams -Credential $UserCredentials
Connect-ExchangeOnline -ManagedIdentity -Organization office365itpros.onmicrosoft.com
[Array]$Users = Get-Recipient -RecipientTypeDetails UserMailbox -Filter {Department -eq 'IT'}
$Users | Format-Table DisplayName, WindowsLiveId -AutoSize
ForEach ($User in $Users) {
   Write-Output ("Processing {0} with UPN {1} to assign IT messaging and meeting policies to account" -f $User.DisplayName, $User.WindowsLiveId)
   Grant-CsTeamsMessagingPolicy -Identity $User.WindowsLiveId -Policy 'Advanced' -ErrorAction SilentlyContinue
   Grant-CsTeamsMeetingPolicy -Identity $User.WindowsLiveId -Policy 'IT Department Meeting Policy' -ErrorAction SilentlyContinue
}

Figure 1 shows the output of a test run. This doesn’t really prove anything except that the Write-Output cmdlet worked!

Updating Teams policy assignments in Azure Automation
Figure 1: Updating Teams policy assignments in Azure Automation

To make this runbook operational, we need to publish it and then link the runbook to an Azure automation schedule so that it’s run based on whatever period is deemed appropriate. Monthly seems like a good idea.

Making sure that these kind of boring, repetitive, but valuable checks are done is a good example of where Azure Automation is very useful. Sure, you could run the script interactively once a month, but you’d have to remember to do it and it’s a task likely to be interrupted by more essential (and interesting) work, so it might not get done.

Inching Forward

Microsoft is gradually modernizing the Teams PowerShell module. As each update appears, new possibilities for operational automation emerge. In this instance, we’re making sure that user accounts have the right Teams messaging and meeting policies. I’m sure you can come up with other examples.


Learn how to exploit the data available to Microsoft 365 tenant administrators through the Office 365 for IT Pros eBook. We love figuring out how things work.

]]>
https://office365itpros.com/2022/11/03/teams-policy-assignment-azureauto/feed/ 6 57735
Assigning Permissions to Entra ID Apps to Use the Microsoft Teams PowerShell Module https://office365itpros.com/2022/10/19/teams-administrator-permission-apps/?utm_source=rss&utm_medium=rss&utm_campaign=teams-administrator-permission-apps https://office365itpros.com/2022/10/19/teams-administrator-permission-apps/#respond Wed, 19 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57460

Assign the Teams Administrator Permission to Apps and Automation Accounts

Last week, I explained how to assign the Exchange.ManageAsApp permission to an Azure automation account. The permission allows an automation runbook to sign into Exchange Online using a system-assigned managed identity and run cmdlets in the Exchange Online management module. Essentially, the permission gives the automation account the right to act like an Exchange administrator who signs in interactively to run Exchange Online cmdlets. The upshot is that any script written for the Exchange Online module becomes a candidate to be run by Azure automation.

The same situation applies to the Teams PowerShell module. When someone runs the Connect-MicrosoftTeams cmdlet to connect to the Teams management endpoint in an interactive session, they can use all the rights and permissions held by their Entra ID account. If those rights and permissions include a role like Global administrator or Teams administrator, they can perform management operations for Teams at the tenant level.

Giving an App the Right to Act as an Administrator

To use the cmdlets in the Teams PowerShell module in an Azure automation runbook, the automation account must hold the permission to act as an administrator. This requirement applies to all methods of authentication used when apps use the module in background jobs, including when a runbook uses a managed identity.

A runbook connects to Teams with a managed identity like this:

Connect-MicrosoftTeams -Identity

When authenticating the connection, Entra ID evaluates the roles and permissions held by the automation account that owns the runbook. It’s equivalent to a user when they connect to Teams in an interactive PowerShell session. If the account holds a Teams administrator role, it can run cmdlets to update the Teams tenant configuration, alter policies, and update accounts.

Assigning the Teams Administrator Permission (Role)

In the previous article, we assigned the role to manage Exchange Online (which holds the Exchange.ManageAsApp permission) to the automation account. The same principle applies to Teams. Instead of assigning the role to allow the app to manage Exchange Online, we assign the role to allow it to manage Teams. As explained in the other article, the Entra admin center doesn’t support role assignment to automation accounts, so this must be done using PowerShell.

This code:

  • Connects to the Microsoft Graph.
  • Populates a variable with details of the service principal for the Skype and Teams Tenant Admin API app. This is an enterprise app installed into tenants to allow components like the Teams admin center to manage Teams. Its app id is always 48ac35b8-9aa8-4d74-927d-1f4a14a0b239.
  • Find the Application_access role in the set held by the Skype and Teams Tenant Admin API app.
  • Populate a variable with details of the automation account to use. In this example, the account is called TeamsAutomationAccount.
  • Populate the parameters to make the role assignment.
  • Run the New-MgServicePrincipalAppRoleAssignment cmdlet to assign the Application_access role to the service principal of the automation account.

Here’s the code:

Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All
# Fetch details of the Teams management app
$TeamsApp = Get-MgServicePrincipal -Filter "AppId eq '48ac35b8-9aa8-4d74-927d-1f4a14a0b239'" 
$AppPermission = $TeamsApp.AppRoles | Where-Object {$_.DisplayName -eq "Application_access"} # Create the payload for the assignment
$ManagedIdentityApp = Get-MgServicePrincipal -Filter "displayName eq 'TeamsAutomationAccount'"
$AppRoleAssignment = @{
"PrincipalId" = $ManagedIdentityApp.Id
"ResourceId" = $TeamsApp.Id
"AppRoleId" = $AppPermission.Id }
# Assign the role to the service principal for the managed identity.
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id -BodyParameter $AppRoleAssignment

After the code runs, check the permissions for the automation account and you should find that the Teams administrative role is present (Figure 1).

The Teams administrative role assigned to the service principal for an automation account

Teams administrator permission
Figure 1: The Teams administrative role assigned to the service principal for an automation account

Remember that the access granted only allows the automation account to run administrative operations. Other operations might need consent for a Teams Graph permission.

Working with Teams Policies

The current version (4.7) of the Teams PowerShell module doesn’t support using a managed identity to run the older cmdlets from the Skype for Business Connector (like Get-CsTeamsMessagingPolicy). This is because Microsoft needs to update these cmdlets to support managed identities.

If you need to run the older cmdlets in an automation runbook, you can sign into Teams with an account that holds the Teams administrator role. For instance, you could store the credentials for an account in Azure Key Vault and use those credentials with Connect-MicrosoftTeams.

Azure Automation and Teams

Using an automation account with scheduled runbooks is an excellent way to make sure that administrative jobs get run. Being able to use common Microsoft 365 PowerShell modules like Teams expands the scope of possible work. A managed identity relieves the need to maintain credentials and keeps things more secure. All in all, it’s a nice combination.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/10/19/teams-administrator-permission-apps/feed/ 0 57460
Making Sure Apps Can Run Exchange Online Management Cmdlets https://office365itpros.com/2022/10/13/exchange-online-powershell-app/?utm_source=rss&utm_medium=rss&utm_campaign=exchange-online-powershell-app https://office365itpros.com/2022/10/13/exchange-online-powershell-app/#comments Thu, 13 Oct 2022 01:00:00 +0000 https://office365itpros.com/?p=57424

Using the Exchange.ManageAsApp Permission with Exchange Online PowerShell

Updated 7 December 2022

With the addition of support for managed identities in V3.0 of the Exchange Online management PowerShell module, developers might be more interested in creating Azure Automation runbooks that use the Exchange Online cmdlets to process data like mailboxes. In this discussion, when I refer to a managed identity, I mean a system-assigned managed identity working within an Azure Automation Account. Essentially, a managed identity is a service principal used to access Azure resources that Azure manages automatically. No access is available to the credentials for the managed identity. Like the service principals for other apps, managed identity service principals can hold permissions to allow them access to resources like apps.

As an example, it’s now easy to connect to Exchange Online in a runbook with a command like:

Connect-ExchangeOnline -ManagedIdentity -Organization office365itpros.onmicrosoft.com 

Exchange Online connects using the managed identity owned by the Azure Automation account that’s executing the runbook.

As noted above, before it can do anything interesting after connecting, the managed identity needs permissions. The essential permission for Exchange Online is Exchange.ManageAsApp, which allows an app to run Exchange Online cmdlets as if the app was an administrator account. Service principals for registered apps and managed identities both need this permission to do useful work with Exchange Online cmdlets.

Some Background

In November 2020, Microsoft announced the deprecation of the Outlook REST API. This was part of a wider effort to move developers away from legacy APIs to the Graph. Microsoft also considers Exchange Web Services (EWS) to be a legacy API, but in this instance, the Exchange team focused on the Outlook REST API, which the Graph Outlook Mail API replaces.

At the same time, Microsoft said that they “removed the Exchange app permission from the Azure portal.” The Exchange.ManageAsApp permission is one of the permissions in the Office 365 Exchange Online API. Microsoft’s action didn’t remove the ability to assign the permission to apps in the Azure AD admin center. It just made the process a little harder.

Assigning Exchange.ManageAsApp

To assign the Exchange.ManageAsApp permission to a registered app, select the app in the Registered Apps blade. Go to API permissions to add a permission as normal. When Azure AD displays the range of permissions to select from, click the APIs my organization uses tab, and then type Office 365 Exchange Online into the search box. Azure AD will find the Office 365 Exchange Online API (Figure 1). Note the application identifier shown here. We’ll need this later.

Finding the Office 365 Exchange Online API
Figure 1: Finding the Office 365 Exchange Online API

Now browse the set of permissions in the Office 365 Exchange Online API and select Exchange.ManageAsApp (Figure 2). Make sure that you’ve selected application permissions and click Add permission. When you return to the app details, consent to the assignment, just like you’d do for a Graph API permission.

Adding the Exchange.ManageAsApp permission
Figure 2: Adding the Exchange.ManageAsApp permission

The registered app can now run Exchange Online cmdlets as an administrator. That’s all well and good, but what about a managed identity?

Managed Identities are Different

Unlike registered apps, managed identities show up under the enterprise apps section of the Azure AD admin center. Open enterprise apps and apply a filter to find managed identities (Figure 3).

Selecting managed identities in the Azure AD admin center
Figure 3: Selecting managed identities in the Azure AD admin center

Azure AD lists the Azure automation accounts with managed identities. Select the automation account you want to work with. When you access its permissions, Azure AD tells you that: “The ability to consent to this application is disabled as the app does not require consent. Granting consent only applies to applications requiring permissions to access your resources.” In other words, you can’t assign an API to an automation account, or rather the service principal for the managed identity, through the Azure AD admin center.

Instead, you can do the job with PowerShell using cmdlets from the Microsoft Graph PowerShell SDK. Here’s how:

  • Note the name of the automation account used with the managed identity. In this example, the account name is “ExoAutomationAccount.”
  • Connect to the Graph with the AppRoleAssignment.ReadWrite.All permission.
  • Run the Get-MgServicePrincipal cmdlet to populate a variable with the service principal for the automation account. The filter passed to the cmdlet contains the name of the automation account.
  • Populate a variable with details of the Office 365 Exchange Online enterprise app. Microsoft installs this app for tenants to allow administrative apps to manage Exchange. The app id for the Office 365 Exchange Online app is always 00000002-0000-0ff1-ce00-000000000000.
  • Find the Manage Exchange As Application role in the set held by the Exchange Online application. This role holds the Exchange.ManageAsApp permission, so any app holding the role can use the permission.
  • Create the parameters to assign the role to the managed identity.
  • Use the New-MgServicePrincipalRoleAssignment cmdlet to assign the role.

Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All
Select-MgProfile Beta
$ManagedIdentityApp = Get-MgServicePrincipal -Filter "displayName eq 'ExoAutomationAccount'"
$ExoApp = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
$AppPermission = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}
$AppRoleAssignment = @{
"PrincipalId" = $ManagedIdentityApp.Id
"ResourceId" = $ExoApp.Id
"AppRoleId" = $AppPermission.Id
}
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id -BodyParameter $AppRoleAssignment

The new role assignment is effective immediately. If you make a mistake, you can remove the assignment with the Remove-MgServicePrincipalAppRoleAssignment cmdlet. Here’s how:

[Array]$SPPermissions = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ManagedIdentityApp.Id
$Role = $ExoApp.AppRoles | Where-Object {$_.DisplayName -eq "Manage Exchange As Application"}
$Assignment = $SpPermissions | Where-Object {$_.AppRoleId -eq $Role.Id}
Remove-MgServicePrincipalAppRoleAssignment -AppRoleAssignmentId $Assignment.Id -ServicePrincipalId $ManagedIdentityApp.Id

Administrator Role

The final step is to make sure that Exchange Online recognizes the automation account which hosts the managed identity as an Exchange administrator. This is done by assigning the Exchange Administrator role to the automation account’s app in the Azure AD admin center. Figure 4 shows how to add the assignment of the Exchange administrator role to the app owned by an automation account.

Making sure that the Managed Identity can act as an Exchange administrator
Figure 4: Making sure that the Managed Identity can act as an Exchange administrator

If you don’t assign the Exchange administrator role to the automation account’s app, you’ll see an error telling you that the role assigned to the app isn’t supported in this scenario when you execute the runbook. For example:

The role assigned to application 415e4ba8-635f-4689-b069-22dea1fcfdb3 isn’t supported in this scenario

Assignment a Small Pain

Perhaps Microsoft under-estimated the continuing need to assign the Exchange.ManageAsApp permission to apps when they made their November 2020 announcement. Although it’s a pain to have to go to PowerShell to assign the permission, it’s something that only needs to happen once, so it’s not too bad. I have other more serious things to moan about inside Microsoft 365.


Learn more about how the Microsoft 365 ecosystem really works on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

]]>
https://office365itpros.com/2022/10/13/exchange-online-powershell-app/feed/ 10 57424