Run PowerShell script inside ARM Virtual Machines using C#

Today’s blog post is about a very common task done when administering Virtual Machines in Azure: run a PowerShell script in these Virtual Machines.

In order to run PowerShell scripts in Azure VMs, we have many options that can be used:

  • Use WinRM (Windows Remote Management) to remotely connect to a Virtual Machine and then run the .ps1 script
  • Manually connect to the Virtual Machine using remote desktop and then run the .ps1 script
  • Set a scheduled task in the Virtual Machine to run the .ps1 script

The options to use WinRM or Remote Desktop have a big limitation: You have to open some ports on the Virtual Machine so that you can remotely connect to it. So if you want to run a .ps1 script automatically without opening any ports then you have to find another solution.

The other option to schedule a task on the VM is not the best one as you may want to run the PowerShell script on demand.

It leaves us with another very interesting option which is to use the Custom Script Extension.

Basically, this is a feature that is using the VM agent to allow you to ask the Virtual Machine to automatically download a script from somewhere (ex: A Blob Storage) and ask it to execute the PowerShell script. There are a lot of references (https://azure.microsoft.com/en-us/blog/automating-vm-customization-tasks-using-custom-script-extension/) on how to make that happen using PowerShell but none of this reference actually covers the case on how to do that using C# and specifically for ARM Virtual machine.

So let’s start to write some code to execute a simple line of PowerShell inside an ARM Virtual Machine that has no port open…

The CmdLet that you will see referred a lot is AzureVMCustomScriptExtension that basically allows you to specify a Virtual Machine Name and the path of the script to execute. An easy way to write some .NET code when you know that there is a CmdLet that exists that does what you are trying to achieve is to open IlSpy and have a look at the code…

In our case, as we want to do that for ARM Virtual Machines, you will be able to open the assembly from the following path:

C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ResourceManager\AzureResourceManager\AzureRM.Compute

Just open the Microsoft.Azure.Commands.Compute in ILSpy and open the class named SetAzureVMCustomScriptExtensionCommand: (note that the CmdLet Name is AzureRmVMCustomScriptExtension
but
the actual name of the method has no RM in there…)

As you can see, there is an ExecuteCmdlet method that will contain exactly the code that we need:

So basically, it creates an object of type VirtualMachineExtension that contains all the parameters and then it uses a Virtual Machine Client (include this nugget to use that: https://www.nuget.org/packages/Microsoft.Azure.Management.Compute/11.2.0-prerelease and if you are not familiar with this library, you should refer to : http://www.bradygaster.com/getting-started-with-the-windows-azure-management-libraries ) to ask the Virtual Machine to execute the PowerShell script.

In this case, the CmdLet allows you to specify the URL of the script to execute. If you just want to execute a few lines of PowerShell without having to create a separate file, you can specify that directly in the Settings property of the VirtualMachineExtension object:

string text = string.Format(“powershell \”{0}\””, “THE POWERSHELL COMMAND”);

Hashtable hashtable = new Hashtable();

hashtable.Add(“commandToExecute”, text ?? “”);


VirtualMachineExtension extensionParameters = new
VirtualMachineExtension

{

Location = currentVM.Location,

Publisher = “Microsoft.Compute”,

VirtualMachineExtensionType = “CustomScriptExtension”,

TypeHandlerVersion = “1.4”,

Settings = hashtable,

AutoUpgradeMinorVersion = new
bool?(true)

};


VirtualMachineExtension busyExtensionTest = client.VirtualMachineExtensions.Get(

currentVMRG,currentVM.Name,
“WinRMCustomScriptExtension”, “instanceView”

);

So with only these few lines of C# you can start any .ps1 script.

View more on http://www.azuredockit.com