Old snapshots still billing in Azure
Old backups of disks that no longer exist
Why it happens
Snapshots are taken before a risky change and then forgotten. They keep billing long after the thing they protected is gone.
Snapshots are taken before a risky change and then forgotten. They bill quietly forever, and the disk they protected is often long gone.
Paste this into Resource Graph Explorer in the Azure portal.
resources
| where type =~ 'microsoft.compute/snapshots'
// Scope below matches CloudFinOpsPro policy DEFAULTS: exclusions.protectTag, environment.tagKeys,
// environment.allowedTagValues, and allowUntagged false so an untagged resource counts as production.
// If your policy.yaml differs, edit the three lines below or this will disagree with your own agent.
| where tolower(tostring(coalesce(tags['cfop:protect'], ''))) !in ('true', 'yes', '1')
| extend cfopEnv = tolower(trim(' ', tostring(coalesce(tags['Environment'], tags['environment'], tags['env'], ''))))
| where cfopEnv in ('dev', 'development', 'test', 'qa', 'sandbox', 'nonprod', 'non-prod', 'staging')
| extend sourceDiskId = tolower(tostring(properties.creationData.sourceResourceId))
| extend ageDays = datetime_diff('day', now(), todatetime(properties.timeCreated))
| extend agentCreated = isnotempty(tostring(tags['cfop:origin']))
| extend retentionDays = iff(agentCreated, 14, 90)
| where ageDays >= retentionDays
| join kind=leftouter (
resources
| where type =~ 'microsoft.compute/disks'
| project sourceDiskId = tolower(id), sourceDiskStillExists = 'yes'
) on sourceDiskId
| where isempty(sourceDiskStillExists)
| project name, resourceGroup, ageDays, retentionDays, agentCreated,
sizeGb = toint(properties.diskSizeGB), sourceDiskId
| order by ageDays descThe join is the important half and the previous version of this query did not have it. A snapshot whose source disk still exists is a deliberate backup, not waste, and the agent will not touch one: the code calls that check the only thing standing between this rule and deleting every backup in the estate. Without the join this lists your live backups and tells you the agent proposes deleting them. The two retention windows are not cosmetic either. A snapshot the agent took itself before deleting a disk carries a cfop:origin tag and is reclaimed after 14 days; anyone else's needs 90, so publishing a flat 90 hid the agent's own snapshots for the 76 days in between. Verified against the reference subscription: at 14 days it returns nothing, because those snapshots are ten days old, and lowering the window to 1 day returns exactly the seven whose source disks are gone and correctly leaves out the eighth, whose disk is still there.
Applies to microsoft.compute/snapshotsWhat the agent does about it
A snapshot past its retention window whose source disk no longer exists.
And what stops it
It has to see the same thing on three consecutive runs before it will act, it stays away from anything a person changed recently, and it will not touch a resource unless you tagged it non-production. Untagged counts as production. Every refusal is written down with the rule that caused it.
Related checks
NetApp capacity pool larger than its volumes need
An Azure NetApp Files capacity pool provisioned well above the capacity its volumes have claimed, billing for tebibytes nothing can use.
Premium SSD paying to burst and never bursting
A Premium SSD with on-demand bursting switched on that has not burst once in the window, so the flat monthly enablement fee is buying a capability the workload never reaches for.
Hot-tier blob data that nothing has touched
Blob data held in the Hot access tier on an account that recorded no transactions at all over the window, where the Cool tier would bill less for the same bytes.