queries/finops/finops-ungoverned-snapshots.json
|
{ "metadata": { "name": "FinOps - Ungoverned managed disk snapshots", "description": "Detect Azure managed-disk snapshots older than the configured threshold (default 90 days) that are not retained by an exemption tag and are not managed by Azure Backup or a Recovery Services Vault.", "version": "1.0.0" }, "queries": [ { "guid": "f9b14e6c-1c4d-4f0a-9d2c-7b6a1f3e0a18", "ruleId": "finops-ungoverned-snapshot", "category": "Cost", "subcategory": "Ungoverned snapshots", "severity": "Medium", "text": "Managed disk snapshots older than {{SnapshotAgeThresholdDays}} days with no retention tag or backup-vault attribution", "queryable": true, "graph": "resources | where type =~ 'microsoft.compute/snapshots' | extend createdTime = todatetime(properties.timeCreated), managedByRef = tostring(managedBy), tagKeys = bag_keys(tags), tagsLower = tolower(tostring(tags)) | extend hasExemptionTag = tagsLower contains 'keep' or tagsLower contains 'retention' or tagsLower contains 'keep-until' or tagsLower contains 'do-not-delete', hasVaultAttribution = (tagsLower contains 'rsvaultbackup') or (tagsLower contains 'backupvaultname') or isnotempty(managedByRef) | where createdTime < ago({{SnapshotAgeThresholdDays}}d) and hasExemptionTag == false and hasVaultAttribution == false | extend ageDays = toint(datetime_diff('day', now(), createdTime)) | project id, name, type, resourceGroup, subscriptionId, location, detectedReason = strcat('Managed disk snapshot is ', ageDays, ' days old with no retention tag (keep/retention/keep-until/do-not-delete) and no backup-vault attribution. No restore activity could be inferred from ARG metadata.'), compliant = false" } ] } |