If you delete an Azure VM, Azure won't automatically remove the associated resources, such as the attached data disks, vNics, or the boot diagnostics disk storage container. However, with the help of PowerShell, you can easily remove all these VM-associated objects.

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
Remove one or many Azure VMs at once.

Remove one or many Azure VMs at once.

4 Comments
  1. RTZ 6 years ago

    Thanks.

    DRY Run would be great addition to this 🙂

  2. Rogier Dijkman 5 years ago

    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!

  3. ankit 4 years ago

    Not working. Please Help urgently much needed

  4. nick 3 years ago

    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.

Leave a reply

Please enclose code in pre tags

Your email address will not be published.

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account