Orphaned Azure resources: the disks, IPs and snapshots you still pay for

27 Jul 2026

Orphaned Azure resources: the disks, IPs and snapshots you still pay for

Deleting a virtual machine in Azure does not delete what was attached to it. The OS disk, the data disks, the public IP that was reserved for it, the last snapshot someone took "just before the change" — all of it survives the VM and keeps billing. These orphans are the most reliable form of cloud waste there is: no user to upset, no risk to reclaiming, and yet almost every subscription carries a layer of them, because nothing in the portal shows you a resource whose parent is gone.

24/7
an idle disk still bills
~€3.50
/mo per idle standard public IP
1 field
managedBy tells you it's orphaned
0 risk
nobody is using what's detached

The usual suspects

Orphans cluster in a handful of resource types. These four cover the vast majority of what you'll find, and each has a precise signal that says "nobody owns me anymore":

Resource The orphan signal Why it lingers
Managed disks diskState = Unattached and no managedBy. VM deleted with "keep disks", or a data disk detached and never removed.
Public IPs No ipConfiguration — reserved but not associated. Standard SKU IPs bill even when idle; the NIC or load balancer is gone.
Snapshots Old timeCreated, source disk long gone. Taken "before a change", never labelled, never expired. They just pile up.
NICs & NSGs NIC with no VM; NSG/route table with no subnet or NIC attached. Mostly free, but they clutter the inventory and hide the ones that cost.
The pattern
Every orphan is the second half of a change that only did its first half. Someone deleted the VM (first half) and never reclaimed the disk (second half). The portal is organised by resource type, not by lifecycle, so the abandoned second halves never appear on one screen — which is exactly why they survive for years.

Where the data lives: managedBy and ipConfiguration

You don't need a usage-metrics pipeline to find orphans — the association is right there on the resource. Azure Resource Graph can sweep every subscription in one query, and the tell-tale property is different per type but always present. A disk that belongs to a VM has a managedBy pointing at that VM's resource ID; an orphaned disk has managedBy = null and properties.diskState = 'Unattached'. A live public IP has an ipConfiguration; an orphaned one has none. The query below is the whole audit for disks:

Resources
| where type == 'microsoft.compute/disks'
| where isnull(managedBy) and properties.diskState == 'Unattached'
| project name, resourceGroup, sku.name, properties.diskSizeGB, location

Three things worth knowing before you act on the output:

  • Unattached is not always dead. A disk detached during a planned migration is legitimately unattached for a week. Filter on timeCreated or a "last detached" age, not just the state, so you reclaim the forgotten ones and leave the in-flight ones alone.
  • Snapshots are cheaper than disks but never expire themselves. They bill on used size, not provisioned, so one snapshot is small — but Azure has no TTL on them, so the count only grows. Age is the right axis here.
  • Basic-SKU public IPs are free; Standard-SKU are not. The migration to Standard made idle IPs a real line item. Check the SKU before you assume an unassociated IP is harmless.
TRIAGE: EVERY DISK, IP AND SNAPSHOT LANDS IN ONE BUCKET All standalone resources detached & old, no owner detached but recent attached & in use delete now confirm with owner keep

Triage: three buckets, three actions

Delete now. Unattached disks and unassociated IPs older than your grace window, and snapshots whose source disk no longer exists. Nobody is using them and nothing is at risk. Snapshot the disk first if you're nervous about the data — a snapshot costs a fraction of a live disk — then delete the disk. This bucket is pure savings and the safest cleanup in Azure.

Confirm with the owner. Resources that are detached but recent, or that carry a tag pointing at a team or project. A one-line message to the owning team ("this disk has been unattached for two weeks — safe to delete?") turns a guess into a decision. Tag hygiene pays off exactly here: untagged orphans are the ones nobody can vouch for.

Keep — but read the bill anyway. Attached, in-use resources are out of scope for this audit, with one asterisk: being used doesn't mean being used efficiently. An oversized Premium disk on a VM that needs Standard is a different kind of waste — the kind you find by reading spend by resource, not by attachment state. That drill-down is covered in our guide to Azure cost access.

Key takeaway
Orphaned resources are the Azure equivalent of a dormant licence: a recurring charge for something with no user behind it. The same discipline applies — it's not a one-time cleanup, it's a monthly report. The orphans refill at the rate your infrastructure changes.

Make it a report, not a spring-clean

Run the Resource Graph queries once and you'll reclaim a real number this month. But the process gap that created the orphans is still open, refilling the list with every VM someone tears down. The durable fix is a standing report — standalone resources × attachment state × age — reviewed monthly, the same way you'd review dormant licensed users. GraphPaaS keeps that view per tenant, refreshed automatically: unattached disks, idle public IPs and stale snapshots are surfaced next to your Azure spend, so the monthly review is reading a list, not writing KQL. For MSPs, the same sweep runs across every subscription in every tenant you manage — the orphans nobody would ever have found by clicking through 20 portals.

GraphPaaS

See the orphaned disks, IPs and snapshots still billing in your Azure subscriptions — surfaced next to what they cost, refreshed on every sync.

Find your orphaned resources →

Newsletter

Get the next article by email

Practical M365 & Azure cost pieces like this one. No spam, unsubscribe anytime.