Azure Cost Optimisation Series - Identify Orphaned Disks Using PowerShell

Hello all,

 

Neil Bird here again, I am a Premier Field Engineer (PFE) in the UK Cloud & Infrastructure team. I am passionate about helping customers get the most out of Microsoft Azure and Windows Server technologies. This is the first in a series of blog posts where I will be sharing some information, scripts and approaches to help with Azure Cost Optimisation for IaaS.

 

Many (if not all) of the organisations I speak to have a strategy to migrate to public cloud in the near future, whether they are migrating their entire data centre or have a hybrid-cloud strategy, it is very easy for them to benefit from the increased Agility, Availability and Hyper-Scale offered by Microsoft Azure.

 

Azure provides the ability for customers to be able to very quickly and easily expand (scale horizontally) or shrink services dynamically depending on their business requirements. This elasticity and Azure's "pay as you go" model are a key business enabler, providing organisation with the flexibility to react to market conditions, new opportunities or internal projects with lightning speed.

 

For Infrastructure as a Service (IaaS) workloads, if you are spinning up (deploying) and tearing down (deleting) virtual machines (VMs) on a regular basis, there is a safety feature built into Azure whereby a VMs Disks are NOT automatically deleted when the VM itself is removed. This scenario (NOT deleting the disks) happens when a VM is deleted without deleting it's parent Resource Group, which is often the case if you have multiple VMs in the same Resource Group, possibly for RBAC delegation purposes.

 

For example, if you were to select a single VM in the Azure Portal and delete it (without deleting the Resource Group), this would mean that the OS Disk, Data Disk(s) and Network Interface associated with that VM would NOT automatically be deleted, these Disks effectively become "Orphaned" and would still incur storage costs in your subscription(s).

 

  1. When you delete a virtual machine (VM) in Azure, by default, any disks that are attached to the VM aren't deleted. This feature helps to prevent data loss due to the unintentional deletion of VMs. After a VM is deleted, you will continue to pay for unattached disks.

 

This behaviour and some example PowerShell code to identify Orphaned Unmanaged and Managed Disks (disks that are no longer associated with a VM) are described in the following support article: /en-us/azure/virtual-machines/windows/find-unattached-disks

 

The sample PowerShell code included in the article above is very useful, however I received feedback from a couple of enterprise customers in the UK that it did NOT provide sufficient information for them to be able to make an informed decision as to whether it is safe to delete a disk or not. i.e - the "Disk URI" alone is not enough information.

 

Following this feedback, I decided to create a more comprehensive PowerShell script that provides the following functionality:

 

  1. Export data to a CSV File.
    (particularly useful if you have multiple subscriptions to audit, as the script appends audit data to the same file)
  2. Output additional properties of the Orphaned Disks.
    (such as "Disk Size", "Last Modified" and "Standard / Premium" Type and "Disk Space Used in GB" for Unmanaged Disks") .
  3. Ability to select the Target Azure Subscription from a list / GUI.
    (The subscription name can also be passed as a parameter if preferred)
  4. Integrate checking for both Managed and Unmanaged Disks into a single script.
  5. If Delete parameter is set to $True, display an additional User Confirmation Prompt prior to deleting Orphaned Disks.
    (additional script safety mechanism)

 

I wanted to share my PowerShell script with you, so that other customers can benefit from the additional disk information that can be exported when you audit your subscription(s) to Identify Orphaned Disks, the script can also Optionally Delete the disks that are identified; this is controlled using a script parameter that is off by default.

 
Example screenshot of the script in action:

 
Dependencies:

The script requires: "AzureRM" Module v3.2 or above.

  1. Install-Module AzureRM

 

Example Syntax to Execute the Script:

Audit Mode:

  1. .\azure-get-orphaned-disks.ps1

Audit and Optional Delete Mode: (The script still displays a yes/no prompt to confirm deletion)

  1. .\azure-get-orphaned-disks.ps1 -DeleteUnattachedDisks $True

 

Excluding a Managed Disk: After reviewing the CSV Audit Report, if you would like to exclude one or more Managed Disks from being deleted, this is possible by creating a "Delete Resource Lock" on the Managed Disk(s). This might be relevant for scenarios such as Azure DevTest Labs that could have Managed Data Disks that are not currently attached to a VM. Or you might have a Disk that is required but is not always attached to a VM. To create a "Delete Resource Lock" from the Azure Portal, find the Managed Disk --> Settings --> Locks --> "Add". Give the Lock a Name and select the Lock type as "Delete", then "OK" to save.

 

Excluding an Entire Storage Account: After reviewing the CSV Audit Report, if you would like to exclude a specific Storage Account (SA) from being processed by the script, it is possible to temporarily create a "Read Only Lock" on the individual SA. This would prevent the deletion of All Unmanaged (VHD) Disks in that SA. This can be done from the Azure Portal, find the Storage Account --> Settings --> Locks --> "Add". Give the Lock a Name and select the Lock type as "Read-only", then "OK" to save. After running the script, you could delete the Read-only Lock from the SA. Note - Please make sure you do not impact other services if you create a Read-only lock on a SA, you may need to arrange a maintenance window if applicable.

 

Download Link:

Download the PowerShell Script from the TechNet Gallery

 

The goal and purpose of this script is to enable you to make an informed decision about an Orphaned Disk in your subscription(s) before deleting it. However, please make sure you perform enough due diligence during your audit / analysis and that you have backups in place prior to deleting any disks.

 

Feedback: Please let me know if this PowerShell script is useful by posting a comment below.

 

I hope you can use it to help optimise the cost of your Azure subscription(s) by deleting orphaned disks that are no longer required.
 

View other blog posts in the Azure IaaS - Cost Optimisation Series https://aka.ms/AzureIaaSCostOptimisation

 
Cheers
Neil

Comments

  • Anonymous
    February 21, 2018
    Hi,Your script will cause PS to crash when working with a container that has a large amount of BLOB's in it. You need to use the BLOB parameter on Get-AzureStorageBlob to do the filtering on the Azure side. I.E Get-AzureStorageBlob -Container $container.Name -Context $context -Blob *.vhd | Where-Object { $_.BlobType -eq 'PageBlob' }ThanksBen
    • Anonymous
      February 22, 2018
      Hi Ben,Thanks for your comment and input. I have not come across the issue you are describing where PowerShell crashes if there is a large number of Blobs in a Container within a Storage Account.I have had a look at the Open Issues for Azure PowerShell in GitHub and there is nothing relating to the 'Get-AzureStorageBlob' cmd-let causing PowerShell to crash. I would recommend that you raise a New Issue in GitHub for this problem and/or open a Support Case with Microsoft, so that we can investigate.Hope this helps.RegardsNeil
    • Anonymous
      July 11, 2018
      Hi Ben,Further to your comment back in February, a colleague of mine (Shaf Mahmood) informed me that another customer has reported the issue you described, when there is a very large number of blobs in a storage account. I have therefore updated the script to use the "-Blob *.vhd" parameter when calling "Get-AzureStorageBlob". I have also asked them to raise this a new issue in GitHub.Thanks for your comment.RegardsNeil