A: The process was provided by one of the customers (Richard Knott from Ascertus):

It utilizes CustomScriptExtension

Classic cmdlet: https://docs.microsoft.com/en-us/azure/virtual-machines/virtual-machines-windows-extensions-customscript

ARM cmdlet: https://docs.microsoft.com/en-us/powershell/resourcemanager/azurerm.compute/v2.3.0/set-azurermvmcustomscriptextension

First the PowerShell script needs to be uploaded to blob storage. This is from a script we use that generates a windows Open File Dialog, and allows the user to select the storage account etc. The Azure Portal also gives a UI for deploying:

$file = Get-ChildItem $OpenFileDialog.FileName
Set-AzureStorageBlobContent -Blob $file.Name -Container $ContainerName -File $OpenFileDialog.FileName -Context $StorageContex -Force

Then the script needs to be passed to the VM. This is from our script that loops though all the VM’s in a Subscription, so we’ve already got the VM object:

Set-AzureVMCustomScriptExtension -ContainerName $ContainerName -StorageAccountName $StorageAccountName -FileName $file.Name -VM $AzureVMObject | Update-AzureVM -Verbose

-- Note: Similar Powershell cmdlet is available for ARM machines: Set-AzureRmVMCustomScriptExtension

We used these to deploy a Powershell module for deploying windows Updates.

And here’s the actual script that runs on the machine (we also have some logging which I’ve excluded):

#Setup variables
$directoryPath= “C:\Packages\CloudMonixWindowsAgent”
#check if already downloaded
if ((Test-Path $zipfile) -eq $false)
        (Get-Date -Format "yyyy-MM-dd HH:mm:ss") +" getting " +$source | Out-File $LogFile -Append -Force
        $wc = New-Object System.Net.WebClient
        #if you need to add credetials
        $wc.Credentials = new-object System.Net.NetworkCredential("username", "password", "domain")
        $wc.DownloadFile($source, $zipfile)
        #error handleing
        (Get-Date -Format "yyyy-MM-dd HH:mm:ss") +" ERROR " + $_.Exception.Message + ":" + $_.Exception.ItemName | Out-File $LogFile -Append -Force
#create unzip folder
If ((Test-Path $directoryPath) -eq $False)
    md $directoryPath
$shell = new-object -com shell.application
$zip = $shell.NameSpace($zipfile)
foreach($item in $zip.items())
start-process $directoryPath\install.cmd -WorkingDirectory $directoryPath  -Wait