normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

How to execute PowerShell scripts inside Azure VMs from external

There are some ways to execute PowerShell scripts inside Azure VMs such like PowerShell remoting. Recently, it comes up cool feature to execute scripts inside Azure VMs easily. This article introduces how to manage that.

Execute PowerShell scripts inside Azure VMs from Azure Portal

Go to Azure Portal and choose an Azure VM among your VMs, so you can find "Run command" menu from left menus. Next, choose "RunPowerShellScript" menu, so you can execute PowerShell scripts like below.
f:id:waritohutsu:20180627100025p:plain
I have already located a text file into "F;\temp\hello.txt" path on Azure VM before executing above scripts to take this screenshot. This means you can manage files inside Azure VMs.
Here is diagram for this scenario. We send HTTP requests as REST API call to VM Agent and the agent execute your PowerShell scripts inside the VM.
f:id:waritohutsu:20180628041811p:plain

Execute PowerShell scripts inside Azure VMs from client machines

You can execute the scripts with PowerShell command named Invoke-AzureRmVMRunCommand. Here is diagram for this scenario. f:id:waritohutsu:20180628042618p:plain
Follow commands snippet below in your local machine PowerShell ISE to execute your scripts inside VM.

$rgname = 'your vm resource group'
$vmname = 'your vm name'
$localmachineScript = 'PowerShell script file on your local machine like script-test.ps1'
Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname -CommandId 'RunPowerShellScript' -ScriptPath $localmachineScript -Parameter @{"arg1" = "var1";"arg2" = "var2"} -Debug 

Confirm your Azure PowerShell module version and authentication if the script doesn't work well.

Execute PowerShell scripts inside Azure VMs from Azure Automation

At first, update Azure Automation modules for your Azure Automation account. Go to your Azure Automation account and choose "Modules" menu from left menu and click "Update Azure Modules" to be latest versions like below.
f:id:waritohutsu:20180627102437p:plain

Next, It's needed to locate your script into downloadable place such like Azure Storage, because we can't locate any files into Azure Automation runtime environment. In this case, I have located a script file into "https://change-your-storage-account-name.blob.core.windows.net/scripts/script-test.ps1" and the content is same with above. Here is diagram for this scenario.
f:id:waritohutsu:20180628043548p:plain

Finally, create a Runbook for the script like below and execute it. This need to authenticate to Azure AD.

$connection = Get-AutomationConnection -Name "AzureRunAsConnection"
Write-Output $connection
Add-AzureRMAccount -ServicePrincipal -Tenant $connection.TenantID -ApplicationId $connection.ApplicationID -CertificateThumbprint $connection.CertificateThumbprint

$rgname = 'your vm resource group'
$vmname = 'your vm name'
$localmachineScript = 'PowerShell script file on your local machine like script-test.ps1'
wget "https://automationbackupstorage.blob.core.windows.net/scripts/$localmachineScript" -outfile $localmachineScript 
Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname -CommandId 'RunPowerShellScript' -ScriptPath $localmachineScript -Parameter @{"arg1" = "var1";"arg2" = "var2"} -Debug 

How to handle exceptions for the scripts inside Azure VMs

It's needed to handle errors when you will integrate this scripts execution into your workflow. I have updated 'script-test.ps1' script like below,

cd F:\temp
type hello.txt
throw "Error trying to do a task @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"

Here is a result of the script execution.

PS D:\temp> $rgname = 'your vm resource group'
$vmname = 'your vm name'
$localmachineScript = 'script-test.ps1'
$result = Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname -CommandId 'RunPowerShellScript' -ScriptPath $localmachineScript -Parameter @{"arg1" = "var1";"arg2" = "var2"} -Debug 
DEBUG: 6:28:11 PM - InvokeAzureRmVMRunCommand begin processing with ParameterSet 'DefaultParameter'.

...

DEBUG: ============================ HTTP REQUEST ============================

HTTP Method:
POST

Absolute Uri:
...

Headers:
x-ms-client-request-id        : f0edfe29-5abf-4d7f-9d83-8c98b3e59891
accept-language               : en-US

Body:
{
  "commandId": "RunPowerShellScript",
  "script": [
    "cd F:\\temp",
    "type hello.txt",
    "throw \"Error trying to do a task @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\""
  ],
  "parameters": [
    {
      "name": "arg1",
      "value": "var1"
    },
    {
      "name": "arg2",
      "value": "var2"
    }
  ]
}


DEBUG: ============================ HTTP RESPONSE ============================

Status Code:
OK

...

Body:
{
  "startTime": "2018-06-26T18:28:14.5508701-07:00",
  "endTime": "2018-06-26T18:28:36.3646186-07:00",
  "status": "Failed",
  "error": {
    "code": "VMExtensionProvisioningError",
    "message": "VM has reported a failure when processing extension 'RunCommandWindows'. Error message: \"Finished executing command\"."
  },
  "name": "bc901040-54ad-4ff1-a8ee-c9794b7a34cb"
}


DEBUG: AzureQoSEvent: CommandName - Invoke-AzureRmVMRunCommand; IsSuccess - True; Duration - 00:00:33.0677753; Exception - ;
DEBUG: Finish sending metric.
DEBUG: 6:28:45 PM - InvokeAzureRmVMRunCommand end processing.
DEBUG: 6:28:45 PM - InvokeAzureRmVMRunCommand end processing.
Invoke-AzureRmVMRunCommand : Long running operation failed with status 'Failed'. Additional Info:'VM has reported a failure when processing extension 'RunCommandWindows'. Error message: 
"Finished executing command".'
ErrorCode: VMExtensionProvisioningError
ErrorMessage: VM has reported a failure when processing extension 'RunCommandWindows'. Error message: "Finished executing command".
StartTime: 6/26/2018 6:28:14 PM
EndTime: 6/26/2018 6:28:36 PM
OperationID: bc901040-54ad-4ff1-a8ee-c9794b7a34cb
Status: Failed
At line:1 char:1
+ Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Invoke-AzureRmVMRunCommand], ComputeCloudException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.Compute.Automation.InvokeAzureRmVMRunCommand
 
DEBUG: AzureQoSEvent: CommandName - Invoke-AzureRmVMRunCommand; IsSuccess - False; Duration - 00:00:33.0677753; Exception - Microsoft.Azure.Commands.Compute.Common.ComputeCloudException: Long
 running operation failed with status 'Failed'. Additional Info:'VM has reported a failure when processing extension 'RunCommandWindows'. Error message: "Finished executing command".'
ErrorCode: VMExtensionProvisioningError
ErrorMessage: VM has reported a failure when processing extension 'RunCommandWindows'. Error message: "Finished executing command".
StartTime: 6/26/2018 6:28:14 PM
EndTime: 6/26/2018 6:28:36 PM
OperationID: bc901040-54ad-4ff1-a8ee-c9794b7a34cb
Status: Failed ---> Microsoft.Rest.Azure.CloudException: Long running operation failed with status 'Failed'. Additional Info:'VM has reported a failure when processing extension 'RunCommandWi
ndows'. Error message: "Finished executing command".'
   at Microsoft.Rest.ClientRuntime.Azure.LRO.AzureLRO`2.CheckForErrors()
   at Microsoft.Rest.ClientRuntime.Azure.LRO.AzureLRO`2.<StartPollingAsync>d__17.MoveNext()
...
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.Azure.Management.Compute.VirtualMachinesOperationsExtensions.RunCommand(IVirtualMachinesOperations operations, String resourceGroupName, String vmName, RunCommandInput paramet
ers)
   at Microsoft.Azure.Commands.Compute.Automation.InvokeAzureRmVMRunCommand.<ExecuteCmdlet>b__0_0()
   at Microsoft.Azure.Commands.Compute.ComputeClientBaseCmdlet.ExecuteClientAction(Action action)
   --- End of inner exception stack trace ---
   at Microsoft.Azure.Commands.Compute.ComputeClientBaseCmdlet.ExecuteClientAction(Action action)
   at Microsoft.WindowsAzure.Commands.Utilities.Common.AzurePSCmdlet.ProcessRecord();
DEBUG: Finish sending metric.
DEBUG: 6:28:47 PM - InvokeAzureRmVMRunCommand end processing.
DEBUG: 6:28:47 PM - InvokeAzureRmVMRunCommand end processing.

PS D:\temp> $result

It seems to be difficult to handle the errors, because there are no contents including message of "Error trying to do a task @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@". In this case, -ErrorVariable option should be useful. Update the script again and execute it like below.

PS D:\temp> $rgname = 'your vm resource group'
$vmname = 'your vm name'
$localmachineScript = 'script-test.ps1'
Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname -CommandId 'RunPowerShellScript' -ScriptPath $localmachineScript -Parameter @{"arg1" = "var1";"arg2" = "var2"} -ErrorVariable result
echo "============================="
$result.Count
echo "============================="
$result
echo "============================="
$result[1]

Invoke-AzureRmVMRunCommand : Long running operation failed with status 'Failed'. Additional Info:'VM has reported a failure when processing extension 'RunCommandWindows'. Error message: 
"Finished executing command".'
ErrorCode: VMExtensionProvisioningError
ErrorMessage: VM has reported a failure when processing extension 'RunCommandWindows'. Error message: "Finished executing command".
StartTime: 6/26/2018 6:35:02 PM
EndTime: 6/26/2018 6:35:17 PM
OperationID: 2ea46d42-2523-4f23-9135-9a595f62f656
Status: Failed
At line:1 char:1
+ Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Invoke-AzureRmVMRunCommand], ComputeCloudException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.Compute.Automation.InvokeAzureRmVMRunCommand
 
=============================
1
=============================
Invoke-AzureRmVMRunCommand : Long running operation failed with status 'Failed'. Additional Info:'VM has reported a failure when processing extension 'RunCommandWindows'. Error message: 
"Finished executing command".'
ErrorCode: VMExtensionProvisioningError
ErrorMessage: VM has reported a failure when processing extension 'RunCommandWindows'. Error message: "Finished executing command".
StartTime: 6/26/2018 6:35:02 PM
EndTime: 6/26/2018 6:35:17 PM
OperationID: 2ea46d42-2523-4f23-9135-9a595f62f656
Status: Failed
At line:1 char:1
+ Invoke-AzureRmVMRunCommand -ResourceGroupName $rgname -Name $vmname - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : CloseError: (:) [Invoke-AzureRmVMRunCommand], ComputeCloudException
    + FullyQualifiedErrorId : Microsoft.Azure.Commands.Compute.Automation.InvokeAzureRmVMRunCommand
 
=============================

PS D:\temp> $result

Unfortunately, we can't take error messages from inside PowerShell scripts but we can find there are errors or not. We should use both Azure Automation logs and logs inside Azure VMs.