VMware Snapshot Management with PowerCLI: Detect, Report, and Clean Up

Quick answer: report first, remove only an approved selection

Snapshot age is a review signal, not permission to delete. Start with a scoped inventory, keep stable object IDs in the report, and separate backup-owner approval from any cleanup action. The examples below default to reporting or preview.

Jump to inventory setup, CSV export, guarded removal, or troubleshooting.

Introduction

Snapshots are a powerful tool in any vSphere environment, but unmanaged snapshots can consume storage, degrade performance, and cause backup failures. Manually reviewing snapshots across hundreds of VMs is inefficient. PowerCLI provides full visibility and control over snapshot lifecycle management.

In this article, you’ll learn to:

  • Inventory snapshots visible to the connected account in one explicitly selected vCenter.
  • Record VM and snapshot IDs, creation time, reported size, and age.
  • Export a read-only report for owner and backup-team review.
  • Preview one explicitly selected snapshot before an approved removal.
  • Schedule reporting independently from cleanup.

Step 1: List All Snapshots in vCenter

Run the setup and report helper below in the same reviewed PowerCLI session. Use the PowerCLI installation guide for your supported environment and approved authentication; never put passwords into the article examples. Inventory is limited by the account’s visibility.

$ErrorActionPreference = 'Stop'
$vc = Connect-VIServer -Server 'vcenter.example.com'
if (@($vc).Count -ne 1) { throw 'Select exactly one vCenter connection.' }

$snapshots = @(
    foreach ($vm in @(Get-VM -Server $vc -ErrorAction Stop)) {
        Get-Snapshot -VM $vm -Server $vc -ErrorAction Stop
    }
)
$snapshots

Build a reusable report helper. Names are labels; retain the vCenter, VM ID, and snapshot ID when discussing a removal. The helper does not infer who created a snapshot or whether a backup product still needs it.

function ConvertTo-SnapshotAudit {
    param(
        [Parameter(Mandatory)][AllowEmptyCollection()][object[]]$Snapshots,
        [Parameter(Mandatory)][string]$VCenterName
    )
    $observedAt = Get-Date
    foreach ($snapshot in $Snapshots) {
        [pscustomobject]@{
            vCenter = $VCenterName
            VM = $snapshot.VM.Name
            VMId = $snapshot.VM.Id
            Snapshot = $snapshot.Name
            SnapshotId = $snapshot.Id
            Created = $snapshot.Created
            AgeHours = [math]::Round(($observedAt - $snapshot.Created).TotalHours, 1)
            ReportedSizeMB = $snapshot.SizeMB
        }
    }
}
$report = @(ConvertTo-SnapshotAudit -Snapshots $snapshots -VCenterName $vc.Name)
$report | Sort-Object Created

Step 2: Review Snapshot Age (Hours)

# Example review threshold, not permission to delete.
$reviewAgeHours = 24
$report | Where-Object { $_.AgeHours -ge $reviewAgeHours } | Sort-Object Created

Broadcom recommends not retaining a single snapshot beyond 72 hours. Use a tighter alert threshold where workload or backup policy requires it. Age alone does not authorize deletion, and a weekly-only audit can miss that window.

$report | Where-Object { $_.AgeHours -ge 72 } | Sort-Object Created

Step 3: Export Snapshot Report to CSV

Run from an approved writable report folder. This creates a new local CSV and refuses to overwrite an existing file. Import text columns as text when opening inventory CSVs in spreadsheet software.

if ($report.Count -eq 0) {
    Write-Output 'No visible snapshots found; no CSV written.'
} else {
    $fileName = 'snapshot-audit-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.csv'
    $csvPath = Join-Path -Path (Get-Location).Path -ChildPath $fileName
    $report | Export-Csv -LiteralPath $csvPath -NoTypeInformation -NoClobber -Encoding UTF8 -ErrorAction Stop
    Write-Output "Report saved to $csvPath"
}

Step 4: Generate an HTML Snapshot Dashboard (Optional)

if ($report.Count -eq 0) {
    Write-Output 'No visible snapshots found; no HTML report written.'
} else {
    # ConvertTo-Html encodes text values; do not pre-encode them a second time.
    $htmlRows = $report | Select-Object vCenter, VM, VMId, Snapshot, SnapshotId, AgeHours, ReportedSizeMB
    $fileName = 'snapshot-audit-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.html'
    $htmlPath = Join-Path -Path (Get-Location).Path -ChildPath $fileName
    $htmlRows | ConvertTo-Html -Title 'Snapshot Audit' |
        Out-File -LiteralPath $htmlPath -NoClobber -Encoding UTF8 -ErrorAction Stop
    Write-Output "Report saved to $htmlPath"
}

Step 5: Preview One Approved Snapshot Before Removal

Before opting in, confirm the application/backup owner’s approval, current backup activity, datastore headroom, and the change window. Removing a snapshot commits its changes; it is not a revert. This example deliberately selects one VM and snapshot by ID and does not request removal of child snapshots. Recheck the selection immediately before applying.

$ApplyChanges = $false
$approvedVmId = 'REPLACE_WITH_VM_ID_FROM_REPORT'
$approvedSnapshotId = 'REPLACE_WITH_SNAPSHOT_ID_FROM_REPORT'

$selectedVMs = @(Get-VM -Server $vc -Id $approvedVmId -ErrorAction Stop)
if ($selectedVMs.Count -ne 1) { throw 'Expected exactly one approved VM.' }
$selectedSnapshots = @(
    Get-Snapshot -Server $vc -VM $selectedVMs[0] -Id $approvedSnapshotId -ErrorAction Stop
)
if ($selectedSnapshots.Count -ne 1) { throw 'Expected exactly one approved snapshot.' }
$approvedSnapshot = $selectedSnapshots[0]
$approvedSnapshot | Select-Object VM, Name, Id, Created, SizeMB | Format-List

if ($ApplyChanges) {
    $confirmation = Read-Host "Type $approvedSnapshotId to approve removal of this one snapshot"
    if ($confirmation -cne $approvedSnapshotId) { throw 'Removal cancelled.' }
    Remove-Snapshot -Snapshot $approvedSnapshot -ErrorAction Stop
} else {
    Write-Output 'Preview only. No snapshot removed.'
}

Powered-off VMs still need review; their power state does not establish that a snapshot is disposable. This alternative is report-only and does not broaden the approved removal above.

$poweredOffSnapshots = @(
    foreach ($vm in @(Get-VM -Server $vc -ErrorAction Stop | Where-Object { $_.PowerState -eq 'PoweredOff' })) {
        Get-Snapshot -Server $vc -VM $vm -ErrorAction Stop
    }
)
ConvertTo-SnapshotAudit -Snapshots $poweredOffSnapshots -VCenterName $vc.Name

Diagram: Snapshot Lifecycle Workflow

Snapshot workflow: inventory one selected vCenter and record VM and snapshot IDs, then export a read-only report. A separate approved change requires owner approval, backup and capacity checks, preview of one exact selection, explicit opt-in, and typed confirmation. Scheduled jobs report only; no automatic deletion.
Reporting and removal are separate workflows. Age or power state prompts review—not deletion. One removal requires owner approval, exact-ID preview, explicit opt-in, and typed confirmation; scheduled audits remain read-only.

Use Case: Scheduled Read-Only Snapshot Audit

  1. Choose a reporting frequency that detects policy exceptions before their retention deadline.
  2. Run the explicit connection setup and report helper under the approved automation identity.
  3. Refresh inventory, write a new report, and route exceptions to the application and backup owners.
  4. Keep cleanup out of the scheduled audit; handle an approved removal as a separate change.

Example reporting body, after the Step 1 setup and helper. Configure unattended authentication and logging through the VMware scripting scheduler guide. This job should report or fail visibly, not prompt or delete infrastructure.

# Refresh inventory; do not reuse a report from an earlier session.
$snapshots = @(
    foreach ($vm in @(Get-VM -Server $vc -ErrorAction Stop)) {
        Get-Snapshot -Server $vc -VM $vm -ErrorAction Stop
    }
)
$report = @(ConvertTo-SnapshotAudit -Snapshots $snapshots -VCenterName $vc.Name)
if ($report.Count -eq 0) {
    Write-Output 'Audit complete: no visible snapshots found.'
} else {
    $fileName = 'snapshot-audit-' + (Get-Date -Format 'yyyyMMdd-HHmmss-fff') + '.csv'
    $csvPath = Join-Path -Path (Get-Location).Path -ChildPath $fileName
    $report | Export-Csv -LiteralPath $csvPath -NoTypeInformation -NoClobber -Encoding UTF8 -ErrorAction Stop
    $report | Where-Object { $_.AgeHours -ge 24 } | Sort-Object Created
}
# No snapshot-removal command belongs in this reporting job.

Tips and Best Practices

  • Snapshots are short-lived change records, not independent backups. Retention exceptions need an owner and an exit plan.
  • Investigate age, growth, datastore capacity, and backup activity together. Reported SizeMB is not a promise of reclaimable space or consolidation duration.
  • Do not interrupt a running removal/consolidation task. Confirm task completion and application health before starting another change.
  • Keep VM and snapshot IDs with approvals. Never infer that a snapshot is orphaned solely from its name or age.
  • Use Get-Snapshot for visible snapshot inventory and Remove-Snapshot only for reviewed selections; investigate untracked delta files and consolidation warnings separately.

Troubleshooting

SymptomCheck before changing anything
Removal fails or stallsInspect vCenter task errors, backup activity, locks, and datastore headroom. Do not cancel consolidation or launch repeated removals blindly.
No snapshots returnedVerify the selected vCenter, VM scope, account visibility, and query errors. An empty query is not proof that no delta disks or consolidation work remain.
Size missing or uncertainGet-Snapshot size reporting requires Datastore/Browse datastore permission. Check permissions and storage state; do not invent a size from a blank field.
GUI and report disagreeRe-query the same VM ID and inspect snapshot/consolidation status. Get-View is not a universal refresh or orphan-cleanup command.
Owner unclearCorrelate retained events, change records, and backup-job history. The basic snapshot report does not establish ownership.

Keep exploring

Choose your next step

Continue with the path that best matches the architecture or operating challenge in front of you.

1 thought on “VMware Snapshot Management with PowerCLI: Detect, Report, and Clean Up”

Leave a Reply

Discover more from Digital Thought Disruption

Subscribe now to keep reading and get access to the full archive.

Continue reading