Run script inside Azure VM from PowerShell

Today I was working on a way to initialize and format disks inside an Azure VM from PowerShell, specifically outside the VM itself.

The two most common ways to accomplish this are:

One of my key requirements was to not have the VM itself go and grab the script file to run; I wanted to pass a scriptblock to my VM from PowerShell instead. This immediately ruled out Set-AzVMCustomScriptExtension, as it needs a file that is accessible to the VM in order to run.

I had originally thought the same about Invoke-AzVmRunCommand. The example I saw for the ‘-ScriptPath’ parameter were all from a storage account or something similar. However after testing, I found that this ScriptPath could be local to MY computer, where I was running the PowerShell, not the VM itself. The command will inject the contents of the script file into the VM.

In order to simulate the “scriptblock” effect without having too many files, I put my code into a script file at runtime (so that it would be wherever the user was running the prompt), referenced it, and then removed it afterwards, like this:

# Build a command that will be run inside the VM.
$remoteCommand =
#Get first disk that is raw with the lowest disk number (because we may not know what number it will be)
# F volume
Get-Disk | Where-Object partitionstyle -eq 'raw' | Sort-Object Number | Select-Object -first 1 | Initialize-Disk -PartitionStyle GPT  -confirm:$false -PassThru |
New-Partition -DriveLetter 'F' -UseMaximumSize |
Format-Volume -FileSystem NTFS -NewFileSystemLabel "F volume" -Confirm:$false
# Save the command to a local file
Set-Content -Path .\DriveCommand.ps1 -Value $remoteCommand
# Invoke the command on the VM, using the local file
Invoke-AzureRmVMRunCommand -Name $ -ResourceGroupName $vm.ResourceGroupName -CommandId 'RunPowerShellScript' -ScriptPath .\DriveCommand.ps1
# Clean-up the local file
Remove-Item .\DriveCommand.ps1


7 thoughts to “Run script inside Azure VM from PowerShell”

  1. Hello,
    Thanks for your ‘remoteCommand’ option for setting the -ScriptPath, it really helped me as I was searching for a whole day before finding this. What I cannot figure out though is how to get the complete output of the script, every command line script that I put in the ‘script block’ comes back with the same result, even when I put wrong code. The script does what I want it to do, but the Output is always the same and is not detailed enough for me. I am trying to get the console output from the VM, do you have any idea how to do this.

    1. With the PowerShell script running inside the VM in my example, I’m not actually putting anything on the output stream, so I am not expecting to receive anything back from where I’m actually calling Invoke-AzVMRunCommand (direct or automation runbook).

      What you need to make sure you’re doing is putting what you want to receive as output on the output stream, inside the script, with “Write-Output”.

      Here’s an example from stackoverflow.

  2. Oh, so close, I’m trying to use Invoke-AzVmRunCommand or some other way to run a command inside of the VM in an automation runbook. It has been a bit of a challenge.

  3. Hi Jeff,

    Great post. I’m trying this method from inside a Runbook and I seem to be stuck with the following error when I try to invoke the file I’ve saved locally:

    Invoke-AzVMRunCommand : The entity was not found in this Azure location.
    ErrorCode: NotFound
    ErrorMessage: The entity was not found in this Azure location.
    StatusCode: 404
    ReasonPhrase: Not Found
    OperationID : a118417a-586d-46b4-b501-2283af11f9c3
    At line:210 char:1
    + Invoke-AzVMRunCommand -Name $vm.Name -ResourceGroupName $vm.ResourceG …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : CloseError: (:) [Invoke-AzVMRunCommand], ComputeCloudException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.Compute.Automation.InvokeAzureRmVMRunCommand

    If I run get-content immediately before it, it pulls down the contents of the .ps1 file without any issues. Here’s an extract of the code. $ScriptBlockSQLStop is a long list of SQL services that need to be stopped:

    Set-Content -Path $PSScriptRoot\ScriptBlockSQLStop.ps1 -Value $ScriptBlockSQLStop
    $vm = Get-AzVM -Name “TEST-VM-Name”
    Invoke-AzVMRunCommand -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -CommandID “TEUTESTUKSDB03_SQL_Stop” -ScriptPath .\ScriptBlockSQLStop.ps1

    Any advice would be appreciated.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.