Recently I was working on a project that was migrating an application from AWS to Azure, one of the outcomes of the project was to vastly simplify the setup when running on Azure so we settled on a running predominantly on Azure Durable Function orchestrating the application and all the processing being done on a Virtual Machine Scale Set in Azure.

The first thing to know about working with Virtual Machine Scale Sets in Azure is that they are a very hands off, set and forget type of resource, only have a minimal number of Virtual Machines running and then scaling out and in as the workload dictates. For instance, the initial set of Virtual Machines that the Scale Set is created with has local accounts created, the new ones added in when scaling don’t, also, installing applications to them is all done via PowerShell scripts that are run as part of a Virtual Machine Extension (more details on installing application to a Virtual Machine Scale Set can be found here). Since everything else was logging to Application Insights we thought it would be a good idea to see if we could also log the installation of the applications via the Custom Extensions to Application Insights too so we could have confidence that everything is running and setup ready to accept requests once scaled.

Overview

Unfortunately there is no default PowerShell module that allows logging to Application Insights, there is an API however that can be used, but on investigation that needed credentials to be managed (not so much of an issue if using Managed Identity in Azure but still a long winded process) but what I was after was a solution that allowed me to only use the Instrumentation Key, after some googling and reading a few blog posts and some StackOverflow issues I found that we can use the .Net Microsoft.ApplicationInsights.dll to do the logging and we only need to supply the Instrumentation Key. I’ll go into some more detail below.

The PowerShell

So now that I’ve got the dll that will do the logging, the PowerShell needs to follow the Try\Catch approach to either log what is happening, or catch the error and log that.

The first piece, simply loads the dll and creates the object.

$AI = "$PSScriptRoot\Microsoft.ApplicationInsights.dll"
[Reflection.Assembly]::LoadFile($AI)

$InstrumentationKey = ""
$TelClient = New-Object "Microsoft.ApplicationInsights.TelemetryClient"
$TelClient.InstrumentationKey = $InstrumentationKey

From here, we are ready to start logging our code to Application Insights using the $TelClient.TrackEvent object.

try {
$TelClient.TrackEvent("Hello, I am VM10... Beginning installation of application")
$TelClient.Flush()
}
catch {  
    $TelException = New-Object "Microsoft.ApplicationInsights.DataContracts.ExceptionTelemetry"
    $TelException.Exception = $_.Exception
    $TelClient.TrackException($TelException)
    $TelClient.Flush()
}

As you can tell, the $TelClient.TrackEvent will write that to Application Insights so it is important to have that at each step to log everything that the script is doing, also with the checks at the end of the installation to write a final message to Application Insights that the newly added VM is ready to accept requests.

Running this on a Virtual Machine Scale Set

You have probably seen that the PowerShell requires the Microsoft.ApplicationInsights.dll to be in the same directory as where your script is running. That was a bit of a chicken and egg scenario, we needed the file their to start the logging, but how do we log downloading the file? That was the first step of the process so we needed to know if that step worked or not. Fortunately, this is where the Azure DevOps pipeline came in handy.

- task: AzureVmssDeployment@0
  inputs: 
    azureSubscription: azureSubName
    action: 'Configure application startup'
    vmssName: 'ScaleSet1'
    vmssOsType: 'Windows'
    customScriptDirectory: '$(Pipeline.Workspace)/helpers
    customScript: 'install.ps1'
    customScriptArguments: '-InstrumentationKey "($InstrumentationKey)"'
    customScriptStorageAccount: 'storageaccount1'

What this task does is, it take the /helpers folder from the pipelines working directory and copies it to the storage account supplied in the customScriptStorageAccount to a zip file that will then get downloaded and extracted on the Virtual Machine. Since the Microsoft.ApplicationInsights.dll also resides in the helpers folder along with the scripts it will also get copied up the Storage Account and in turn, downloaded to the Virtual Machine ready to be loaded into the PowerShell script to start the logging of your script.

In Closing

When it comes to being able to log your PowerShell scripts to Application Insights the sky is the limit as to what you can do, the important thing here I found, especially when running scripts on remote systems is that there is now visibility of that execution and I can sleep a bit better at night knowing that if anything goes wrong I have single place to look for the logs of my scripts execution.