Allways HyPe is in your extended network
TLDR: I love talking nerdy with people! ;)
Azure Arc and Azure Automation
Alright, let’s talk about something I’ve been messing around with for a while - Azure Arc and Azure Automation. This post kicks off a series where I’ll break down my journey of automating tasks with Azure Arc, what worked, what didn’t, and what I learned the hard way. If you’ve ever struggled with making automation work in a hybrid cloud setup, buckle up.
One of the reasons that I’ve been so excited and passionate about Azure Arc-enabling servers, whether they’re on-prem or in another cloud, is due to the numerous ways that the Azure platform functionality is unlocked without having to go all-in on Azure VMs. And honestly, that flexibility is a game-changer.
If you've ever talked to me, you know I’m all about iterating toward a goal instead of expecting a perfect setup from day one. My job constantly reminds me that the dream scenario isn’t always feasible immediately, so I focus on moving the needle forward, piece by piece. One of the big things that I've been focused on is creating a timeline for migration of various services to cloud-native. Recently, I had the opportunity to do just that.
There are numerous ways to update photos for your users, but what if you’ve got some somewhat unique constrains and are looking to disrupt the current workflow as little as possible before implementing a more mature and finalized solution? That’s what we’ll looking at doing today!
The fantastic blog post from SysManSquad: Using PowerShell and Graph to update Azure AD user photos in bulk got me started, but there were a couple of elements I tweaked to fit my use case.
What we’ll be covering:
Creating a Hybrid Worker group for our Azure Arc- enabled server
Building an Automation Account
Setting up a Managed Identity
Assigning permissions to the Managed Identity
Creating a Runbook in the Automation Account
Fixing some really annoying leaky data issues
Hybrid Workers: Why i love them ✩°。🎧 ✩°。⋆⸜
▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄
One of the big reasons that I am such a fan of enrolling on-prem servers into Azure Arc is the ability to leverage Azure Automation Runbooks with the help of Hybrid Runbook Workers. Hybrid Workers allow the Runbook to run on the host server locally, giving the Azure Automation Runbook access to the resources that exist on the host. That means I can migrate scheduled tasks without moving everything to Azure all at once. It’s like a cheat code for modernization.
Managed Identities: BECAUSE secrets are the worst ✩ ♬₊.⋆☾⋆⁺₊✧✩♬
▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄
I’ve been looking forward to working with Managed Identities for a while, and the addition of system-assigned managed identities in Azure Automation accounts was icing on the cake for me. No certs, no password rotation, and I can ditch managing service principal secrets. Just clean and simple authentication!
Azure Automation Setup
Initial setup ‧₊˚🖇️✩ ₊˚🎧⊹ ♡
▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄
The simplest method to get started is to build everything with PowerShell. We’ll be creating a Resource Group, Automation Account and system assigned Managed Identity. If you already have a Resource Group that you’re using for your Azure Arc-enabled servers, go ahead and skip the creation of the Resource Group and use the one that you already have and create the Automation Account inside of it.
#create Resource Group, Automation Account, and System Managed Identity
Connect-AzAccount -Subscription 'Your Subscription Name'
#create Resource Group
$rgname = "rgAzureTesting"
$location = "westus2"
New-AzResourceGroup -Name $rgname -Location $location
#create Automation Account
$accountName = 'aaAzureTesting'
$rgName = 'rgAzureTesting'
$location = 'westus2'
New-AzAutomationAccount -Name $accountName -ResourceGroupName $rgName -Location $location
#create System Managed Identity
$accountName = 'aaAzureTesting'
$rgName = 'rgAzureTesting'
Set-AzAutomationAccount -Name $accountName -ResourceGroupName $rgName -AssignSystemIdentity
Next, we need to add the permissions to the Managed Identity
#Connect to Graph for assigning permissions
$TenantId = 'Your Tenant Id'
Connect-MgGraph -TenantId $TenantId
#Assign the Graph Permissions needed for updating user info
$GraphApp = Get-MgServicePrincipal -Filter "AppId eq '00000003-0000-0000-c000-000000000000'"
$ManagedIdentityApp = Get-MgServicePrincipal -Filter "displayName eq 'aaAzureAzureTesting'"
$Permissions = @(
"Directory.Read.All",
"User.ReadWrite.All",
"ProfilePhoto.ReadWrite.All"
)
$AppRoles = $GraphApp.AppRoles | Where-Object `
{($_.Value -in $Permissions) -and ($_.AllowedMemberTypes -contains "Application")}
foreach ($AppRole in $AppRoles) {
$AppRoleAssignment = @{
"PrincipalId" = $ManagedIdentityApp.id
"ResourceId" = $GraphApp.id
"AppRoleId" = $AppRole.id
}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $AppRoleAssignment.PrincipalId `
-BodyParameter $AppRoleAssignment -verbose
}
Ditching PowerShell Modules for Invoke-RestMethod ✩ ♬₊.⋆☾⋆⁺₊✧✩♬
▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄
One of my biggest frustrations? Constantly keeping PowerShell modules up to date. I finally said enough and switched to Invoke-RestMethod
for direct Graph API calls. No more version mismatches, no more dependency headaches, just raw API calls.
I took a lot of inspiration from Sean’s Comparing Invoke-RestMethod to the PowerShell SDK blog post, which breaks down why REST API calls are often the better move.
More than just automating profile photos, this setup is a stepping stone for managing anything in Graph at scale.
Fixing My Mess: Leaky Data Issues
At one point, I realized my automation was pulling way more data than I actually needed. Turns out, since array lists have been my go-to for everything, I was using them instead of an ordered hash table. This caused my data filtering to be about as effective as a broken sieve.
Switching to an ordered hash table cleaned things up and gave me much better control over what was getting processed. Another lifesaver? Splatting. If you’re calling Invoke-RestMethod
a lot, use splatting. It makes API calls cleaner, easier to read, and less prone to errors.
I learned a ton from this PowerShell tips article on handling Graph API calls properly.
I’ll be doing a deep dive on all of this in a future post because, wow, was this a frustrating lesson to learn.
Hybrid worker SETUP ✩ ♬₊.⋆☾⋆⁺₊✧✩♬
▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄
Now, we’ll want to add the Hybrid Worker to Azure Automation account that we just created. Under the Process Automation blade, select Hybrid worker groups and Create hybrid worker group.
And now we’ll see the Hybrid Worker Group listed
Runbooks
Test the runbook ✩°。🎧 ✩°。⋆⸜
▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄▀▄
Alright, so we did all of this setup so that we can run PowerShell scripts on the Azure Arc’d servers as if they were Azure VMs. Since these scripts are running on on-prem servers, we’re going to need to make sure that the script runs locally on the server.
When testing out a Runbook with a Managed Identity, running the script locally with the Managed Identity won’t work as a local run won’t have access to the Managed Identity resource. I’d recommend testing a script that won’t need those credentials and then move forward with using the Managed Identity in the Automation Account.
Start with a basic script that doesn’t require credentials.
Once that runs, integrate the Managed Identity.
Validate that it has the correct permissions before deploying at scale.
Pro Tip: If a Runbook works locally but not in Azure, it’s probably an authentication or permissions issue. Triple-check the Managed Identity’s role assignments.
Wrapping Up
Azure Arc + Hybrid Workers + Automation Accounts + Managed Identities = a ridiculously powerful automation setup.
If you want to check out the script I built for automating user photo updates, you can find it here.
This was just the first step. Stay tuned for the next post where I’ll break down leaky data fixes, structured API calls, and better debugging techniques.
If you’ve been doing something similar, hit me up! I’d love to hear how you’re automating things (and what pitfalls you’ve run into).
┊˚➶ 。˚ ☁
My IT "Top 8"
•
My IT "Top 8" •
My IT Toolbox
My current “Top 8” when it comes to IT resources…
…catch ‘em all! ;)