A while ago I wrote a blog post on how you can run a PowerShell or Bash script against multiple Azure Virtual Machines (VMs) using Run Command. In this blog post we are going to have a look on how you can schedule and run PowerShell and Bash scripts against Azure virtual machines (VMs) using Azure Automation. For this we are going to use Azure Run Command and Azure Automation.
Run Command can run a PowerShell or shell script within an Azure VM remotely by using the Azure Virtual Machine Agent. This scenario is especially useful when you need to run scripts against Azure VMs where you do not have network access. You use Run Command for Azure VMs through the Azure portal, REST API, Azure CLI, or PowerShell. Like I showed you in my blog post on Microsoft Tech Community.
Azure Automation delivers a cloud-based automation, operating system updates, and configuration service that supports consistent management across your Azure and non-Azure environments. It offers a couple of distinctive features, in this blog we are going to use the process automation feature, which allows you to run automation inform of PowerShell runbooks. We are going to use Azure Automation to schedule the scripts to run on Azure VMs.
Set up Azure Automation Account
First, you will need to create an Azure Automation account, this is very straight forward.
On the Advanced tab, you can configure the managed identity option for your new Automation account. This is the identity under which the runbook can sign in into Azure PowerShell for example. In this case I am going to use a System assigned identity.
Check out Microsoft Docs for more information.
Set up a system assigned managed identity, role assignment and grant permissions
After you have created the Azure Automation account, you can grant permissions to the managed identity by using Azure role-based access control (Azure RBAC). The managed identity is authenticated with Azure AD, so you don’t have to store any credentials in code.
To grant permissions for the Azure VM you want the script to run against, you can create a new Azure role assignment under identity.
Here you can select the scope this can be on a subscription, resource group, or even resource level. In my case I want to run this script only on Azure VMs in a specific resource group, if you want to run it on a subscription level, you can change the scope.
Create an Azure Automation PowerShell Runbook
Now you can create a new Azure Automation PowerShell runbook, which will host the script you are going to run on a schedule.
For this runbook we are going to use a PowerShell type and runtime version 7.1. With PowerShell 7 and higher we can make use of the “-parallel” parameter and some other cool features.
Now you can copy and paste the following script. You need to change the $scriptCode variable with the code you want to run against your Azure VMs.
Write-Output "Connecting to azure via Connect-AzAccount -Identity"
Connect-AzAccount -Identity
Write-Output "Successfully connected with Automation account's Managed Identity"
# Script which should run inside the Azure VMs (Edit this)
$scriptCode = '<PASTE CODE HERE>'
#Get all Azure VMs which are in running state and are running Windows
$myAzureVMs = Get-AzVM -status | Where-Object {$_.PowerState -eq "VM running" -and $_.StorageProfile.OSDisk.OSType -eq "Windows"}
Write-Output "The following VMs are running and are running Windows:"
Write-Output $myAzureVMs.Name
# Run the script against all the listed VMs
Write-Output "Run Script Against Machines"
$myAzureVMs | ForEach-Object {
Invoke-AzVMRunCommand -ResourceGroupName $_.ResourceGroupName -Name $_.Name -CommandId 'RunPowerShellScript' -ScriptString $scriptCode
}
Should look like this:
Make sure after you are done editing, Save and Publish the script.
Run Azure Automation Runbook PowerShell Script against Azure VMs
Now you can start the Runbook and it will run against the Azure VMs the Managed Identity has access to. No local user account and password is required to run this.
Schedule Azure Automation Runbook
If you have tested the runbook, you can now schedule it, by linking it to a schedule. For that press Link to schedule.
You will always be able to edit the schedule or unlink it from the runbook if you don’t need it anymore.
Conclusion
I hope this post was helpful and showed you how you can use Azure Automation to schedule and run PowerShell scripts against Azure VMs using Run Command. If you have any questions or comments, feel free to leave the below.
Tags: Azure, Azure Automation, Azure VM, Azure VMs, Cloud, Microsoft, Microsoft Azure, PowerShell, Runbooks, script, Scripts, Virtualization, Windows Server Last modified: August 9, 2022
Thanks for the detailed write-up. Question: is there a way to return the output of the script executed against each VM in the Azure portal? For example, I’d like to run the ‘uptime’ command across my fleet and have those results appear in the output for the playbook. Thanks in advance.
Hi Ryan,
please try to send the invoke-azvmruncommand to a variable. You can then read the (output) message from this variable….
$result = Invoke-AzVMRunCommand ….
If ($result.Value.Count -gt 0) { Write-Output $result.Value[0].Message }
Have fun….
one question can run the code in the block or just run a .ps1 that on a blob ?
Yes, but you would need to build a script to download the file on to the machine and then run it.
you can save your Powershell script in a Blob and then before you run the Invoke-AzVMRunCommand, download the .ps1 file to the runbook temp folder
Hi, I’m trying to run this code but I ‘m getting the follow error
Invoke-AzVMRunCommand : A parameter cannot be found that matches parameter name ‘ScriptString’.
Where do you run the command?
I too am getting the error:
Invoke-AzVMRunCommand : A parameter cannot be found that matches parameter name ‘ScriptString’.
I am running the command in a Powershell (v7.1) runbook through my Azure Automation account.
Very powerful also in a hybrid environment with the hybrid worker.
You can run, manage, schedule the same scripts but also for on-prem VMs.
I am also getting error A parameter cannot be found that matches parameter name ‘ScriptString’.
Tried with PS 5.1 & 7.1
If I may, I would like to propose a change on your code. Your code is great but it will only show the VMs on the first subscription you are logged in. If you have multiple subscriptions, then I would recommend to add to your code something like this:
# Retrieve all subscriptions
$subscriptions = Get-AzSubscription
# Initialize an array to hold all VM information
$allVMs = @()
foreach ($subscription in $subscriptions) {
# Set the context to the current subscription
Set-AzContext -SubscriptionId $subscription.Id
# Retrieve all VMs in the current subscription
$vms = Get-AzVM
# Add subscription information to each VM object
$vms | ForEach-Object {
$_ | Add-Member -MemberType NoteProperty -Name SubscriptionId -Value $subscription.Id
$_ | Add-Member -MemberType NoteProperty -Name SubscriptionName -Value $subscription.Name
$allVMs += $_
}
}
# Display the list of all VMs with their subscription information
$allVMs | Select-Object -Property Name
Nice work!