Get Ubuntu 16.04 VMs in Azure with PowerShell

Ubuntu 16.04 is going End Of Life at the end of April 2021, so without a paid-for subscription there won’t be any new security updates. We can use a couple of methods in PowerShell to list affected VMs in a tenancy.

Method 1: Image-based Virtual machines

VMs built from images are the easier target - we can quickly look for VMs built on the UbuntuServer image using the SKU 16.04-LTS

1Get-AZVM | where {$_.StorageProfile.ImageReference.Offer -eq "UbuntuServer" 
2                -and $_.StorageProfile.ImageReference.Sku -eq "16.04-LTS"}

Method 2: Manually built VMs

VMs which have been manually installed/modified, or perhaps arrived in Azure via a lift-and-shift operation from elsewhere may not be tied to an image, therefore looking up the OS version tied to the image doesn’t work. In these cases we can use the Azure Agent to interrogate the guest OS.

In the case of Ubuntu 16.04, the file /etc/osrelease contains the Ubuntu version, so we can check for the presence of Ubuntu 16.04 in that file. The cmdlet Invoke-AzVmRunCommand can be used to do this remotely assuming that the logged in account has permissions to do so on the VM.

Invoke-AzVmRunCommand needs a script file to be run, so we’ve prepared the following file and saved it as ubuntuversion.sh.

1cat /etc/osrelease

Breaking down the process, we’re searching each VM in the current context:

1foreach ($VM in Get-AZVM  ....

And filtering to just the Linux VMs …

1Where-Object {$_.StorageProfile.OsDisk.OsType -eq "Linux" 

… which are powered on.

1(Get-AZVM -Name $_.Name -ResourceGroupName $_.ResourceGroupName -Status).Statuses.Code -contains "PowerState/running"

With each of the VMs in this list we’re running our pre-prepared script which returns /etc/osrelease to us

1$result=invoke-azvmruncommand -vmname $VM.Name -ResourceGroupName $VM.ResourceGroupName -CommandId "RunShellScript" -ScriptPath .\ubuntuversion.sh 

And then if that file contains Ubuntu 16.04 we’re writing the VM Name to the console

1 if($result.value.item(0).Message.ToString().Contains("Ubuntu 16.04")){$VM.Name}

So altogether the snippet looks like:

1foreach ($VM in Get-AZVM -Status |
2 Where-Object {$_.StorageProfile.OsDisk.OsType -eq "Linux" -and (Get-AZVM -Name $_.Name -ResourceGroupName $_.ResourceGroupName -Status).Statuses.Code -contains "PowerState/running"}) {
3	$result=invoke-azvmruncommand -vmname $VM.Name -ResourceGroupName $VM.ResourceGroupName -CommandId "RunShellScript" -ScriptPath .\ubuntuversion.sh ; 
4	if($result.value.item(0).Message.ToString().Contains("Ubuntu 16.04")){$VM.Name}
5}

When run this will produce a list of the powered on Azure VMs running Ubuntu 16.04LTS in the current context/subscription of the PowerShell session.

Known Issues

This will fail if

1Long running operation failed with status 'Failed'. Additional Info:'VM 'myVMName' has not reported status for VM agent or extensions. 
2Verify that the OS is up and healthy, the VM has a running VM agent, and that it can establish outbound connections to Azure storage. 
3Please refer to https://aka.ms/vmextensionlinuxtroubleshoot for additional VM agent troubleshooting information.'
4ErrorCode: VMAgentStatusCommunicationError