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 $vm.name -ResourceGroupName $vm.ResourceGroupName -CommandId 'RunPowerShellScript' -ScriptPath .\DriveCommand.ps1 # Clean-up the local file Remove-Item .\DriveCommand.ps1 |
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.
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.
Hi Jeff,
This was a really clever solution which I successfully used.
Thanks for posting!
Joe
Glad it helped you Joe!
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.
Hey Joseph, what you described (Invoke-AzVmRunCommand inside an Automation Runbook) is something I have used consistently – I trigger runbooks from an Azure Site Recovery “Recovery Plan” which interact against VMs in this way.
Here’s an example:
https://github.com/jeffwmiles/arm-azuresiterecovery/blob/master/runbooks/dr-addhostfileentries-runbook.ps1
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.
ErrorTarget:
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.
Thanks
Hi David, sorry for the slow response.
This error is because of your value in the “CommandID” parameter. It must be the value ‘RunPowerShellScript’, rather than your own custom value.
Unfortunately Microsoft doesn’t document this, or other possible values, despite related GitHub issues asking for it: https://github.com/Azure/azure-powershell/issues/7331
Microsoft has (finally?) documented the acceptable values for the CommandId parameter in this article: https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command#available-commands
how to get return from Invoke-AzureRmVMRunCommand , i need output to return in variable so i can use that further
Thanks Jeff it helped me.