Home / Azure

AzureCheat sheetBeginner

Azure VM Run Command cheat sheet: run scripts on Windows and Linux VMs without RDP or SSH

Run PowerShell or shell scripts inside Azure VMs from the CLI when RDP or SSH is broken. Action vs managed Run Command, ready-made commands, permissions, limits and fixes for failed runs.

Deepak Bhardwaj7 min read

Applies toAzure Run Command (action and managed) on Windows and Linux Azure VMs and VM scale sets; Azure CLI; Az PowerShell (Az.Compute)

When to use it

RDP or SSH is broken, a firewall rule locked you out, or you need to run the same check on twenty VMs. Run Command sends a script to the VM through the Azure VM agent and returns the output. You do not need network access to the VM, a public IP or Bastion.

It needs:

  • The Azure VM agent running and reporting Ready.
  • Outbound access from the VM to the Azure platform so the agent can fetch the script and report back.
  • The Microsoft.Compute/virtualMachines/runCommand/action permission (for example, Virtual Machine Contributor). Plain Reader cannot run commands.

Scripts run as SYSTEM on Windows and root on Linux.

Caution

Anyone with Run Command permission has full control of the guest OS. Treat that role assignment like local admin and review who has it.

Two flavours

Action Run Command Managed Run Command
CLI az vm run-command invoke az vm run-command create / show / delete
Best for Quick, one-off checks Longer scripts, async runs, keeping a record
Output Returned in the response (truncated) Instance view, or uploaded to a blob
Concurrency One at a time per VM Several named commands per VM
Stored on VM resource No Yes, until you delete it

Check the current limits (output size, timeout) in the Microsoft documentation before relying on them for long jobs.

Action Run Command: everyday commands

Windows

az vm run-command invoke -g rg-app -n vm-win01 \
  --command-id RunPowerShellScript \
  --scripts "Get-Service | Where-Object Status -eq 'Stopped' | Select-Object -First 20 Name"

Run a local script file:

az vm run-command invoke -g rg-app -n vm-win01 \
  --command-id RunPowerShellScript --scripts @check-disk.ps1

Pass parameters (the script declares a param() block):

az vm run-command invoke -g rg-app -n vm-win01 \
  --command-id RunPowerShellScript \
  --scripts 'param([string]$svc) Get-Service -Name $svc' \
  --parameters "svc=W3SVC"

Linux

az vm run-command invoke -g rg-app -n vm-lnx01 \
  --command-id RunShellScript \
  --scripts "df -h; free -m; systemctl --failed --no-pager"

Read just the output

The response is JSON. Pull out the message text:

az vm run-command invoke -g rg-app -n vm-lnx01 --command-id RunShellScript \
  --scripts "uptime" --query "value[0].message" -o tsv

Built-in command IDs

List what your region offers:

az vm run-command list --location westeurope -o table

Commonly useful ones (names vary by region and over time; check the list):

Command ID OS Use
RunPowerShellScript Windows Any PowerShell script
RunShellScript Linux Any shell script
IPConfig Windows Show IP configuration
EnableRemotePS Windows Turn on PowerShell remoting
RDPSettings Windows Show RDP port and settings
SetRDPPort Windows Change the RDP port
ResetRDPCert Windows Recreate the RDP TLS certificate
DisableNLA Windows Disable Network Level Authentication
EnableAdminAccount Windows Re-enable the local administrator
ifconfig Linux Show network interfaces

Troubleshooting recipes

Problem Command
RDP refused, firewall suspected --scripts "Get-NetFirewallRule -DisplayGroup 'Remote Desktop' | Select DisplayName, Enabled"
RDP service stopped --scripts "Set-Service TermService -StartupType Automatic; Start-Service TermService"
SSH refused --scripts "systemctl status sshd --no-pager; ss -tlnp | grep :22"
Disk full Windows: Get-PSDrive -PSProvider FileSystem; Linux: du -xh / --max-depth=2 | sort -h | tail -20
High CPU Windows: Get-Process | Sort CPU -Desc | Select -First 10; Linux: top -b -n1 | head -20
DNS failing Windows: Resolve-DnsName contoso.com; Linux: getent hosts contoso.com
Outbound test Windows: Test-NetConnection login.microsoftonline.com -Port 443; Linux: curl -sS -o /dev/null -w '%{http_code}' https://login.microsoftonline.com
Recent errors Windows: Get-WinEvent -LogName System -MaxEvents 30 | Where Level -le 2; Linux: journalctl -p err -n 50 --no-pager

Warning

A script that restarts the network stack, changes the firewall or reboots the VM will cut off the Run Command reply. The change may still be applied. Check the result with a second, read-only run.

Managed Run Command

Use it for scripts that run longer, should run asynchronously, or need their output kept.

az vm run-command create -g rg-app --vm-name vm-win01 \
  --name collect-logs \
  --script "Get-WinEvent -LogName Application -MaxEvents 500 | Export-Csv C:\Temp\app.csv" \
  --timeout-in-seconds 1800 \
  --async-execution false

Check status and output:

az vm run-command show -g rg-app --vm-name vm-win01 --name collect-logs \
  --instance-view --query "instanceView.{state:executionState, exit:exitCode, out:output, err:error}"

Send large output to storage instead of the instance view. Both URIs are blob SAS URLs with write permission:

az vm run-command create -g rg-app --vm-name vm-lnx01 --name inventory \
  --script "dpkg -l || rpm -qa" \
  --output-blob-uri "https://stops.blob.core.windows.net/runcmd/inventory.out?<sas>" \
  --error-blob-uri  "https://stops.blob.core.windows.net/runcmd/inventory.err?<sas>"

List and clean up:

az vm run-command list -g rg-app --vm-name vm-win01 -o table
az vm run-command delete -g rg-app --vm-name vm-win01 --name collect-logs --yes

Managed commands stay on the VM resource until deleted. Clean them up so the next operator is not confused by an old failed run.

PowerShell (Az.Compute)

# Action run command
Invoke-AzVMRunCommand -ResourceGroupName rg-app -VMName vm-win01 `
  -CommandId RunPowerShellScript -ScriptPath .\check-disk.ps1

# Managed run command
Set-AzVMRunCommand -ResourceGroupName rg-app -VMName vm-win01 -Location westeurope `
  -RunCommandName collect-logs -SourceScript "Get-Service | Out-String"

Get-AzVMRunCommand -ResourceGroupName rg-app -VMName vm-win01 -RunCommandName collect-logs -Expand InstanceView

Many VMs at once

Run the same check across a resource group:

for vm in $(az vm list -g rg-app --query "[?storageProfile.osDisk.osType=='Linux'].name" -o tsv); do
  echo "== $vm"
  az vm run-command invoke -g rg-app -n "$vm" --command-id RunShellScript \
    --scripts "df -h / | tail -1" --query "value[0].message" -o tsv
done

For scale sets, target an instance:

az vmss run-command invoke -g rg-app -n vmss-web --instance-id 0 \
  --command-id RunShellScript --scripts "systemctl is-active nginx"

When Run Command fails

Error or symptom Cause Fix
AuthorizationFailed on runCommand/action Caller lacks the permission Assign Virtual Machine Contributor or a custom role with that action
Hangs, then times out VM agent not Ready, or VM cannot reach the platform az vm get-instance-view -g rg-app -n vm-win01 --query instanceView.vmAgent; check outbound rules and guest proxy
Conflict, another run in progress An earlier action run is still executing Wait for it, or use a managed run command with a new name
Output cut short Action output is truncated Write to a file and read the tail, or use managed run command with --output-blob-uri
VM stopped or deallocated Agent is not running Start the VM first; for boot failures use Serial Console

Prevent it next time

  • Keep the VM agent healthy and auto-updating; Run Command and most extensions depend on it.
  • Restrict who has runCommand/action and alert on it. The Activity Log records each run, so you can see who ran a command and when.
  • Keep a small library of tested read-only scripts in source control for the checks you run most.
  • Delete managed run commands after use.