This check flags Azure managed disk snapshots older than 14 days. Stale snapshots quietly rack up storage costs and widen your data exposure surface. Audit your snapshots, delete the ones nobody needs, and put a lifecycle policy in place so it does not happen again.
Snapshots are easy to create and even easier to forget. Someone takes a point-in-time copy of a disk before a risky migration, the migration goes fine, and the snapshot sits in the resource group for the next two years. Multiply that by every engineer on your team and you end up with a sprawl of orphaned snapshots that cost money and store sensitive data long after anyone needs them.
The Old Disk Snapshot check (vm_oldsnapshots) looks at your Azure managed disk snapshots and raises a finding when one is more than 14 days old. It is a simple signal, but it points at two real problems: unmanaged cost and unmanaged data retention.
What this check detects
Azure lets you create snapshots of managed disks. A snapshot is a full, read-only, point-in-time copy of a disk, stored as a separate billable resource. This check inspects the timeCreated property of every snapshot in scope and flags any snapshot whose age exceeds 14 days.
It does not look at whether the snapshot is attached to anything, because snapshots are never "attached" the way disks are. A snapshot just exists until you delete it. Age is the proxy the check uses to surface candidates for review.
Note: Snapshots in Azure are billed based on the used size of the source disk, not the provisioned size, and they are charged at the rate of the storage account type you select (Standard HDD, Standard SSD, or Premium SSD). A snapshot of a sparsely used 1 TB disk that only holds 80 GB of data is billed roughly on that 80 GB, but incremental snapshots and full snapshots bill differently, so the math gets fuzzy fast.
Why it matters
The 14-day threshold is not arbitrary. Most legitimate operational snapshots, the ones taken before a patch, a schema change, or a config rollout, have served their purpose within a couple of weeks. Anything older is usually either a forgotten one-off or part of a backup process that should be running through Azure Backup instead of ad hoc snapshots.
Cost creep
A single old snapshot is cheap. Hundreds of them across dozens of subscriptions are not. Snapshot sprawl is one of the most common sources of unexplained storage spend in Azure, precisely because each one is too small to notice on its own. Nobody gets a budget alert for a $4 snapshot, but the aggregate shows up in the monthly bill as a line item nobody can account for.
Data exposure and compliance
A snapshot is a complete copy of a disk. If that disk held customer data, secrets, or regulated information, so does the snapshot. The difference is that the snapshot is often outside your normal access controls and review processes. Anyone with snapshot read and export permissions can generate a SAS URL and download the full VHD.
Warning: Snapshots inherit the data of the source disk at creation time, but they do not inherit your encryption key rotation, your retention policy, or your data classification controls going forward. A snapshot of a disk that was later wiped under a deletion request still contains the original data. That is a real GDPR and CCPA problem if you are not tracking it.
An attacker's shortcut
Disk snapshots are a known data exfiltration path. An attacker who lands a foothold with snapshot permissions does not need to break into a running VM. They snapshot the disk, copy the snapshot to a storage account they control or export it via SAS, and walk away with the data without ever touching the workload or tripping host-level alerts. Old snapshots that nobody is watching make this quieter, because no one notices an extra one in a pile of forgotten ones.
How to fix it
The fix is to review old snapshots and delete the ones you do not need. The review matters, because some old snapshots are legitimate golden images or forensic captures you must keep.
1. List your old snapshots
Find every snapshot older than 14 days across a subscription with the Azure CLI:
az snapshot list \
--query "[?timeCreated < '$(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%SZ')'].{Name:name, RG:resourceGroup, Created:timeCreated, Disk:creationData.sourceResourceId}" \
--output table
On macOS, swap the date command for the BSD syntax:
CUTOFF=$(date -u -v-14d +%Y-%m-%dT%H:%M:%SZ)
az snapshot list \
--query "[?timeCreated < '$CUTOFF'].{Name:name, RG:resourceGroup, Created:timeCreated}" \
--output table
2. Tag before you delete
Before deleting anything, give owners a chance to claim their snapshots. Tag candidates and give them a grace period:
az snapshot update \
--name mySnapshot \
--resource-group myResourceGroup \
--set tags.review_for_deletion=true tags.flagged_on=$(date -u +%Y-%m-%d)
3. Delete the confirmed-stale snapshots
Danger: Deleting a snapshot is irreversible. There is no recycle bin for snapshots. Confirm the snapshot is not the only copy of data you are obligated to retain, and confirm it is not the source for any image or disk you still need, before running the command below.
az snapshot delete \
--name mySnapshot \
--resource-group myResourceGroup
To clean up a reviewed batch, drive deletion from a list you have already vetted:
# snapshots_to_delete.txt holds lines of: resourceGroup snapshotName
while read -r RG NAME; do
echo "Deleting $NAME in $RG"
az snapshot delete --resource-group "$RG" --name "$NAME"
done < snapshots_to_delete.txt
Tip: If you find snapshots you do need to keep but rarely access, move them to a cheaper tier instead of deleting them. Set the snapshot SKU to Standard_LRS for infrequently accessed copies rather than keeping them on premium storage.
az snapshot update \
--name mySnapshot \
--resource-group myResourceGroup \
--sku Standard_LRS
How to prevent it from happening again
Manual cleanup is a treadmill. The goal is to make stale snapshots either impossible to create or automatically removed.
Tag snapshots at creation with an expiry
Bake an expiry date into every snapshot you create, then run a scheduled job that deletes anything past its date. This puts the retention decision in the hands of whoever makes the snapshot:
az snapshot create \
--name preMigration-2024-06-01 \
--resource-group myResourceGroup \
--source /subscriptions/.../disks/myDisk \
--tags expires_on=2024-06-15 owner=team-payments
Enforce tagging with Azure Policy
Use Azure Policy to require an expires_on tag on every snapshot. The policy below denies snapshot creation when the tag is missing:
{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/snapshots"
},
{
"field": "tags['expires_on']",
"exists": "false"
}
]
},
"then": {
"effect": "deny"
}
}
Automate cleanup
Run a scheduled job (Azure Automation runbook, a Function on a timer trigger, or a CI cron job) that deletes snapshots past their tagged expiry:
TODAY=$(date -u +%Y-%m-%d)
az snapshot list --query "[].{Name:name, RG:resourceGroup, Expires:tags.expires_on}" -o tsv |
while IFS=$'\t' read -r NAME RG EXPIRES; do
if [[ -n "$EXPIRES" && "$EXPIRES" < "$TODAY" ]]; then
echo "Expired: $NAME (expired $EXPIRES) - deleting"
az snapshot delete --resource-group "$RG" --name "$NAME"
fi
done
Warning: Test any automated deletion job in a non-production subscription first, and run it in dry-run mode (log only, no delete) for at least one cycle. An overly broad query that misreads a tag can wipe snapshots you meant to keep, and there is no undo.
Use Azure Backup for real backups
If people are creating snapshots as a backup mechanism, that is a sign you need a managed backup process. Azure Backup and the Backup vault handle retention policies, lifecycle, and access control for you, which removes the reason for ad hoc snapshots in the first place.
Best practices
- Treat snapshots as ephemeral by default. If a snapshot needs to live longer than a couple of weeks, that should be a deliberate decision tied to an owner and a reason, not an accident.
- Always tag with an owner and a purpose. An untagged snapshot is an orphan the moment its creator forgets about it. Tags like
owner,purpose, andexpires_onmake cleanup decisions obvious. - Use incremental snapshots where you take repeated snapshots of the same disk. They only store the delta since the last snapshot, which cuts cost significantly versus full snapshots.
- Encrypt snapshots with customer-managed keys if the source disk holds sensitive data, and confirm the encryption carries over. A snapshot is just another copy of your data and deserves the same protection.
- Tighten snapshot permissions. The
Microsoft.Compute/snapshots/beginGetAccess/actionpermission allows generating download URLs. Limit who holds it, because it is the exfiltration path described above. - Run this check continuously. A point-in-time cleanup helps once. Continuous monitoring catches the next snapshot before it becomes the next forgotten one.
Old snapshots are a small problem that compounds. Each one is a few dollars and a copy of your data sitting somewhere it does not need to be. Tag them at creation, expire them automatically, and route real backups through Azure Backup, and this finding stops showing up entirely.

