- Create a certificate-signed RDP shortcut via Group Policy - Fri, Aug 9 2019
- Monitor web server uptime with a PowerShell script - Tue, Aug 6 2019
- How to build a PowerShell inventory script for Windows Servers - Fri, Aug 2 2019
In Azure, bringing up a new virtual machine can't be easier. However, the process to delete an Azure VM is a little more complicated. When a virtual machine is built in Azure, quite a few objects are created that associate with the VM. If you just delete the VM, Azure won't remove these resources.
When a VM is created, depending on the options chosen, five objects are made that might not get cleaned up with the VM itself:
- A boot diagnostics disk storage container
- The operating system disk
- The vNic
- The operating system disk status blob
- Any attached data disks
Cleaning each of these objects up with the VM requires the Azure PowerShell module and a fair amount of PowerShell code. Before we get started, be sure that you have the Azure PowerShell module. You can get this by manually installing it or, if you have Windows Management Framework v5, simply run Find-Module AzureRm | Install-Module to download the latest version from the PowerShell Gallery.
After you install the Azure PowerShell module, you must authenticate to your account by running Login-AzureRmAccount and providing your username and password.
Let's first create a $vm variable. This will represent the VM object itself. We'll periodically retrieve values from this variable while removing various components. To do this, we'll provide the resource group name and the name of the VM itself.
$vmName = 'MYVM' $rgName = 'MyResourceGroup' $vm = Get-AzureRmVM –Name $vmName –ResourceGroupName $rgName Next, we need to get the VM ID. This is required to find the associated boot diagnostics container. $azResourceParams = @{ 'ResourceName' = $vmName 'ResourceType' = 'Microsoft.Compute/virtualMachines' 'ResourceGroupName' = $rgName } $vmResource = Get-AzureRmResource @azResourceParams $vmId = $vmResource.Properties.VmId
Now, we can remove the boot diagnostics storage container with some regular expression magic. Unfortunately, I could find no other, easier way to do this.
$diagSa = [regex]::match($vm.DiagnosticsProfile.bootDiagnostics.storageUri, '^http[s]?://(.+?)\.').groups[1].value $diagContainerName = ('bootdiagnostics-{0}-{1}' -f $vm.Name.ToLower().Substring(0, 9), $vmId) $diagSaRg = (Get-AzureRmStorageAccount | where { $_.StorageAccountName -eq $diagSa }).ResourceGroupName $saParams = @{ 'ResourceGroupName' = $diagSaRg 'Name' = $diagSa } Get-AzureRmStorageAccount @saParams | Get-AzureStorageContainer | where { $_.Name-eq $diagContainerName } | Remove-AzureStorageContainer –Force
After all that work is done, we can now remove the VM itself.
$vm | Remove-AzureRmVM –Force
Then, we can remove the vNic that was attached to the VM.
$vm | Remove-AzureRmNetworkInterface –Force
Now we are ready to remove the operating system disk. Again, this step isn't too pretty, but it gets the job done!
$osDiskUri = $vm.StorageProfile.OSDisk.Vhd.Uri $osDiskContainerName = $osDiskUri.Split('/')[-2] $osDiskStorageAcct = Get-AzureRmStorageAccount | where { $_.StorageAccountName -eq $osDiskUri.Split('/')[2].Split('.')[0] } $osDiskStorageAcct | Remove-AzureStorageBlob -Container $osDiskContainerName -Blob $osDiskUri.Split('/')[-1]
You'll sometimes see a status storage blob that is created with the VM but does not get removed with it. To remove the status storage blob, we'll read the OS disk's storage account and use the storage container that the OS disk was located in to find the blob that ends with ".status" and remove it.
$osDiskStorageAcct | Get-AzureStorageBlob -Container $osDiskContainerName -Blob "$($vm.Name)*.status" | Remove-AzureStorageBlob
If your VM has any attached data disks, you might want to clean those up as well. In this code sample, we are first checking to see if any data disks were connected to the VM. If so, we will iterate over each of the VHD URIs; find the storage account each is located in and the container each is located in, along with the name; and remove them all.
if ($vm.DataDiskNames.Count -gt 0) { Write-Verbose -Message 'Removing data disks...' foreach ($uri in $vm.StorageProfile.DataDisks.Vhd.Uri) { $dataDiskStorageAcct = Get-AzureRmStorageAccount -Name $uri.Split('/')[2].Split('.')[0] $dataDiskStorageAcct | Remove-AzureStorageBlob -Container $uri.Split('/')[-2] -Blob $uri.Split('/')[-1] -ea Ignore } }
If you'd like to get a copy of this code, feel free to download it from my GitHub repository, where I've built a function around this that easily allows you to remove one or many Azure VMs at once.
It can be run like this:
Subscribe to 4sysops newsletter!
$azureCredential = Get-Credential Remove-AzrVirtualMachine –VMName <YourVMName> -ResourceGroupName <YourResourceGroupName> -Verbose -Wait -Credential $azureCredential
Thanks.
DRY Run would be great addition to this 🙂
Hi Adam,
Good Article, I am writing the same kind of function that also checks if managed or unmanaged disks are used.
Your piece about removing the bootdiagnostics was very useful. Regarding the RegEx I have used another solution to get the storage account name:
#saUri=$vm.DiagnosticsProfile.BootDiagnostics.StorageUri
$diagSa=($saUri -split ‘\.’ | Select-Object -First 1) -split ‘//’ | Select-Object -Last 1
Another error that does occur is if you have a dash in the name of the VM exp: sp2013-03 This would result in bootdiagnostics-sp201303-fc8775 so the dash is being removed by Azure.
Based on that info the solution I used is to remove the storage blob based on $vm.vmID in a like function:
Get-AzureStorageContainer | Where-Object { $_.Name -like $vmid }
Keep up the good work!
Not working. Please Help urgently much needed
One issue is that Get-AzureRmResource no longer returns the vmid, so we can't work out the boot diagnostics folder name. Switching everything over to the Get-AzResource commands doesn't help either.