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

Use Case: Scheduled Read-Only Snapshot Audit
- Choose a reporting frequency that detects policy exceptions before their retention deadline.
- Run the explicit connection setup and report helper under the approved automation identity.
- Refresh inventory, write a new report, and route exceptions to the application and backup owners.
- 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
| Symptom | Check before changing anything |
|---|---|
| Removal fails or stalls | Inspect vCenter task errors, backup activity, locks, and datastore headroom. Do not cancel consolidation or launch repeated removals blindly. |
| No snapshots returned | Verify 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 uncertain | Get-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 disagree | Re-query the same VM ID and inspect snapshot/consolidation status. Get-View is not a universal refresh or orphan-cleanup command. |
| Owner unclear | Correlate retained events, change records, and backup-job history. The basic snapshot report does not establish ownership. |
Introduction In disaster recovery planning, power loss, HVAC failure, or critical hardware degradation may require a controlled shutdown of virtual infrastructure. Manually...
1 thought on “VMware Snapshot Management with PowerCLI: Detect, Report, and Clean Up”