Skip to main content
emnode
Cost

Delete managed disks not attached to a VM

An unattached managed disk is a deleted virtual machine's storage that nobody turned off. It bills at the full provisioned size every hour, forever, while serving no workload. This lesson finds every one in the estate, tells the genuinely orphaned from the deliberately parked, and reclaims the spend safely.

13 min·10 sections·AZURE

Last reviewed

Unattached managed disks: the basics

Why does deleting a VM sometimes leave its storage running?

When you delete an Azure virtual machine, its operating-system and data disks are not always deleted with it. A managed disk is an independent resource with its own lifecycle, and unless 'delete with VM' was set on it, it survives the VM and keeps billing. The result is an orphaned disk: a Premium or Standard managed disk that is not attached to anything, costs the same every hour as it did when it ran a workload, and serves nobody. Azure Advisor flags exactly this under the recommendation 'Review disks that aren't attached to a VM' (Recommendation ID 48eda464-1485-4dcf-a674-d0905df5054a).

Managed disks bill on provisioned size and performance tier, not on usage. You pay for the full size of the disk you selected regardless of how much data sits on it, and you pay whether or not it is attached to a running machine. A 128 GB Premium SSD that nobody mounts costs the same as one serving a production database. That is what makes unattached disks the cleanest line in an Azure bill to reclaim: there is no workload to migrate, no capacity to right-size, just storage that should have been deleted and was not.

The signal is two properties on the disk. 'managedBy' holds the resource id of the VM a disk is attached to; on an unattached disk it is null. 'diskState' reports 'Unattached' for a disk that is genuinely idle. Disks that Azure Site Recovery uses for replication look unattached too but report 'ActiveSAS', which is why the Advisor cost workbook's 'Unattached Managed Disks' query deliberately ignores them. The work in this lesson is to list every disk that is truly orphaned, separate the few that are parked on purpose, and delete the rest with a snapshot safety net where you are unsure.

In this lesson you will learn why Azure leaves managed disks behind when virtual machines are deleted, how to find every unattached disk across a subscription, how to tell a genuinely orphaned disk from one that is parked on purpose or used by Azure Site Recovery, and how to reclaim the spend safely with a snapshot net where you need one. It is grounded in the Azure Advisor recommendation 'Review disks that aren't attached to a VM' and the cost workbook's 'Unattached Managed Disks' query.

Fun fact

The disk that outlived the server by two years

Because a managed disk is its own resource, an operating-system disk can keep billing long after the virtual machine it booted is gone, and data disks survive even longer because nothing in the portal nudges you to delete them. Azure has since added a 'delete with VM' option you can set per disk so the storage is cleaned up automatically when the machine is removed, but it is off by default for data disks and for any disk created before the option existed. That default is why almost every long-lived Azure estate has unattached disks it has been paying for since well before anyone now on the team can remember creating them.

Finding orphaned disks across a subscription

Priya runs the platform team at a company that has been on Azure for four years across several subscriptions. The monthly bill has a steady storage line that nobody can fully account for, and Advisor has been quietly showing 'Review disks that aren't attached to a VM' for months.

Rather than open each disk in the portal, Priya lists every managed disk whose 'managedBy' is null and whose 'diskState' is 'Unattached', so the genuinely orphaned disks are separated from anything Site Recovery is using before a single delete is run.

List every disk that is truly unattached: managedBy is null and diskState is Unattached. This skips the ASR replication disks, which report ActiveSAS.

$ az disk list --query "[?managedBy==null && diskState=='Unattached'].{name:name, rg:resourceGroup, gb:diskSizeGb, sku:sku.name}" -o table
Name Rg Gb Sku
------------------ -------------- ---- ---------------
vm-web01_OsDisk rg-legacy-web 128 Premium_LRS
olddb-datadisk-01 rg-legacy-web 1024 Premium_LRS
tempclone-os rg-sandbox 30 Standard_LRS
# Two Premium disks billing full price for VMs deleted long ago. Reclaim these first.

A 1024 GB Premium data disk attached to nothing is the single highest-value line here. Size the reclaim by provisioned GB times tier, biggest first.

How Azure decides a disk is unattached, and why ASR disks are differentdeep dive

Two properties on a managed disk tell the whole story. 'managedBy' holds the resource id of the virtual machine the disk is attached to, and on a free disk it is null. 'diskState' is the disk's own lifecycle status: 'Attached' on a running VM, 'Reserved' when the VM is stopped-deallocated, 'ActiveSAS' when the disk has a live shared access signature against it, and 'Unattached' when it is genuinely idle and available to attach. A disk that is both managedBy null and diskState 'Unattached' is the clean orphan you can reclaim.

The ASR distinction matters because it is the one trap. Azure Site Recovery replicates disks for machines that do not yet have a target VM, so those disks have no managedBy but are not waste: deleting one breaks your disaster-recovery posture. They report diskState 'ActiveSAS' rather than 'Unattached', which is precisely why the Advisor cost workbook's 'Unattached Managed Disks' query filters on diskState 'Unattached' and so ignores them. Filtering on managedBy alone would sweep ASR disks into the list; filtering on diskState 'Unattached' does not.

Azure Advisor evaluates the recommendation 'Review disks that aren't attached to a VM' (Recommendation ID 48eda464-1485-4dcf-a674-d0905df5054a) on a periodic cycle, so a disk you delete does not clear the recommendation the instant you act; the control-plane delete is immediate but Advisor catches up on its next refresh. To prevent the leak reopening, set 'delete with VM' on disks at creation, or run the inventory on a schedule so new orphans are swept before they accumulate.

What is the impact of leaving unattached disks in place?

The direct impact is recurring spend for nothing. A managed disk bills on its provisioned size and performance tier every hour it exists, attached or not, used or not. An unattached Premium SSD of 128 GB is on the order of a low-tens-of-pounds-per-month line, a 1024 GB Premium disk many times that, and the charge continues indefinitely because nothing ever stops it. Across a multi-year estate these accumulate into a four-figure or larger annual run-rate that buys no capacity anyone uses.

The second-order impact is that the leak compounds and hides. Every VM that is deleted without its disks adds another orphan, and because the bill shows a single storage figure rather than a per-disk attribution, the growth is invisible unless someone runs the inventory. Left alone, the line only ever rises, one forgotten machine at a time, which is why this is best treated as a recurring sweep rather than a one-time cleanup.

There is no upside to leaving them. Unlike a VM you might still need or a database you cannot shrink without testing, an unattached disk supports no workload, so the only thing the spend protects is the small chance that a disk holds data someone will want back. That chance is fully covered by a snapshot, which costs a fraction of the live disk, so even the cautious path reclaims almost all of the run-rate.

How do you reclaim unattached-disk spend safely?

Treat it as one loop rather than a per-disk chore. The order matters: inventory and classify before you delete, so you never remove a disk Site Recovery is using or one that is parked on purpose.

1. Inventory every truly unattached disk

List disks where managedBy is null and diskState is 'Unattached', not managedBy alone, so Azure Site Recovery's replication disks (which report 'ActiveSAS') are excluded automatically. Capture provisioned size and SKU for each so the reclaim can be priced and ordered by value.

2. Separate orphans from deliberately parked disks

Most unattached disks are genuine orphans from deleted machines. A few are parked on purpose: a disk kept to re-attach to a rebuilt VM, or a golden image source. Tag those with a clear keep marker and an owner so future sweeps skip them, and treat everything untagged as a candidate for deletion.

3. Snapshot anything you are unsure about, then delete

Where a team cannot confirm a disk holds nothing they want, take an incremental snapshot first. It persists after the disk is gone, bills only for stored data on Standard HDD, and turns an irreversible delete into a reversible one. Then delete the disk and reclaim the full live-disk charge.

4. Stop the leak reopening

Set 'delete with VM' on disks at creation so storage is cleaned up when its machine is removed, and run the unattached-disk inventory on a schedule so any new orphans are swept before they accumulate. The sweep is what turns a one-off recovery into a permanently clean run-rate.

# 1. Inventory: only genuinely unattached disks (managedBy null AND diskState Unattached).
#    This excludes Azure Site Recovery disks, which report diskState 'ActiveSAS'.
az disk list \
  --query "[?managedBy==null && diskState=='Unattached'].{name:name, rg:resourceGroup, gb:diskSizeGb, sku:sku.name}" \
  -o table

# 2. Snapshot-then-delete each orphan, skipping any tagged to keep.
#    Set DELETE=1 only after reviewing the list above; DELETE=0 is a dry run.
DELETE=0
az disk list \
  --query "[?managedBy==null && diskState=='Unattached' && tags.keep!='true'].id" -o tsv \
| while read -r diskId; do
    rg=$(az disk show --ids "$diskId" --query resourceGroup -o tsv)
    name=$(az disk show --ids "$diskId" --query name -o tsv)
    if [ "$DELETE" = "1" ]; then
      # Incremental snapshot as a reversible safety net (bills only for stored data).
      az snapshot create --resource-group "$rg" --name "${name}-pre-delete" \
        --source "$diskId" --incremental true
      az disk delete --ids "$diskId" --yes
      echo "deleted $name (snapshot ${name}-pre-delete kept)"
    else
      echo "dry run: would snapshot and delete $name in $rg"
    fi
  done

Quick quiz

Question 1 of 5

Why does an Azure managed disk keep billing after its virtual machine is deleted?

You can now treat unattached managed disks as a clean, recurring saving rather than a mystery line on the bill: inventory every disk where managedBy is null and diskState is 'Unattached' so Site Recovery disks are excluded, separate the deliberately parked ones with a tag, snapshot anything in doubt, delete the rest to reclaim the full charge, and put the sweep on a schedule so the leak stays shut.

Back to the library