Delete Unattached Azure Storage Account

While creating a VM on Azure, we needed to set up the storage account to store the VHD in a page blob. With the VM Creation Wizard, the user-created storage accounts and the default names are often confusing to assign in a VM. Microsoft introduced the concept of Managing Disks last year which gives you the capability of hosting these VHDs without taking the overhead of managing the storage accounts.

After deleting the VMs, the users are not sure whether they have deleted the associated storage account or not. People often use the same storage account for multiple VHDs as well. So, they’re usually not sure if there is any VHD in the storage still connected to the VM or not, and if it will be safe to delete the storage or not. This is always a big question in a user's mind. Moreover, if the disk is not attached to the VM, the users still need to pay the price for the storage in their billing. In order to solve this issue, I have created the following script.

This script loops over all the storage accounts in your subscription and searches for the page blob with a .VHD extension and checks for the lease. If the lease is released, that means it might be safe to delete the storage. The user needs to authenticate himself/herself in the first step to run this script.

I have kept toDeleteUnattachedVHDs = $false as it will delete only if it's true. With the default values, the script will show you the list of URIs of the VHDs for you to evaluate whether you want to delete it or not. If you change the value of this variable to $true, then it will delete all the storage where the VHDs are not attached to a VM.

  1. Connect-AzureRmAccount  
  2. $toDeleteUnattachedVHDs = $false  
  3. $storageAccounts = Get-AzureRmStorageAccount  
  4. foreach ($storageAccount in $storageAccounts) {  
  5.     $Key = (Get-AzureRmStorageAccountKey -ResourceGroupName $storageAccount.ResourceGroupName -Name $storageAccount.StorageAccountName)[0].Value  
  6.     $context = New-AzureStorageContext -StorageAccountName $storageAccount.StorageAccountName -StorageAccountKey $Key  
  7.     $containers = Get-AzureStorageContainer -Context $context  
  8.     foreach ($container in $containers) {  
  9.         $blobs = Get-AzureStorageBlob -Container $container.Name -Context $context  
  10.         #Fetch all the Page blobs with extension .vhd  
  11.         $blobs | Where-Object {$_.BlobType -eq 'PageBlob' -and $_.Name.EndsWith('.vhd')} | ForEach-Object {  
  12.             #If a Page blob is  attached as disk then LeaseStatus will not be unlocked  
  13.             if ($_.ICloudBlob.Properties.LeaseStatus -eq 'Unlocked') {  
  14.                 if ($toDeleteUnattachedVHDs -eq $true) {  
  15.                     Write-Host "Deleting unattached VHD with Uri: $($_.ICloudBlob.Uri.AbsoluteUri)"  
  16.                     $_ | Remove-AzureStorageBlob -Force  
  17.                     Write-Host "Deleted unattached VHD with Uri: $($_.ICloudBlob.Uri.AbsoluteUri)"  
  18.                 }  
  19.                 else {  
  20.                     $_.ICloudBlob.Uri.AbsoluteUri  
  21.                 }  
  22.             }  
  23.         }  
  24.     }  
  25. }  

Please note that it’s an irreversible action. Thus, I advise the users to make sure that they’re keeping the value of toDeleteUnattachedVHDs = $true only if they’re sure about the deletion. In the first run, it’s always safer to run with the value as $false. Once you have verified the results, you can convert it to $true.