vSphere snapshots capture the state of a virtual machine at a specific point in time — disk, memory (optionally), and configuration. They are the standard safety net before patching, upgrading application stacks, or making configuration changes that may need to be rolled back cleanly.
Managing snapshots through the vSphere Client works for one or two machines, but breaks down the moment you need to act on a group of VMs, audit snapshot age across a datacenter, or automate cleanup as part of a maintenance workflow. PowerCLI exposes the full snapshot lifecycle — create, inspect, revert, consolidate, remove — through commands that compose naturally into scripts.
This guide covers each operation in sequence, adds parameter reference tables for the main cmdlets, and includes a production-grade bulk cleanup script with dry-run support and CSV reporting.
Quick answer
# Create a snapshot before patching
New-Snapshot -VM "SRV-APP-01" -Name "Before patch 2025-03" -Description "Pre-patch state" -Memory $false -Quiesce $false
# List all snapshots for a VM with size and age
Get-Snapshot -VM "SRV-APP-01" | Select-Object Name, Description, Created, SizeGB
# Revert to a named snapshot
$snap = Get-Snapshot -VM "SRV-APP-01" -Name "Before patch 2025-03"
Set-VM -VM "SRV-APP-01" -Snapshot $snap -Confirm:$false
# Remove a specific snapshot
Remove-Snapshot -Snapshot (Get-Snapshot -VM "SRV-APP-01" -Name "Before patch 2025-03") -Confirm:$false
# Find stale snapshots older than 7 days across all VMs
$threshold = (Get-Date).AddDays(-7)
Get-VM | Get-Snapshot | Where-Object { $_.Created -lt $threshold } | Select-Object VM, Name, Created, SizeGB | Format-Table -AutoSize
Prerequisites
- PowerCLI installed and connected to vCenter or ESXi — see How to install VMware PowerCLI and connect to vSphere
- Snapshot operations require VM Power User role or higher on the target VMs
- For quiesce operations: VMware Tools must be installed and running inside the guest
Step 1 — Connect to vCenter
All snapshot operations run in the context of an active session. Connect before executing any commands below:
Connect-VIServer -Server vc01.vsphere.local
-Server explicitly on each cmdlet, or use $global:DefaultVIServers to verify which sessions are active before running bulk operations.
Step 2 — Create a snapshot
Use New-Snapshot to capture the current VM state before a risky change. The -Name parameter is required and should be descriptive enough to identify purpose and date when auditing later.
New-Snapshot parameter reference
| Parameter | Type | Default | Purpose |
|---|---|---|---|
-VM | String / VM object | Required | Target VM name or object from Get-VM |
-Name | String | Required | Snapshot display name — include date for auditability |
-Description | String | “” | Free-text note visible in vSphere Client |
-Memory | Bool | $false | Capture RAM state — required if you need to resume exact running state |
-Quiesce | Bool | $false | Request filesystem quiesce via VMware Tools — reduces write inconsistency for DBs |
-RunAsync | Switch | Off | Returns immediately without waiting for task completion — use for bulk ops |
-Confirm | Bool | $true | Suppress interactive confirmation prompt when $false |
-Server | VIServer | Active session | Explicit vCenter target — important in multi-vCenter environments |
Single VM snapshot before patching
# -Memory $false skips RAM capture — smaller and faster; sufficient for patch rollback
# -Quiesce $false is safe here since we are not dealing with a database VM
New-Snapshot -VM "SRV-APP-01" `
-Name "Before patch 2025-03" `
-Description "Pre-patch snapshot, March maintenance window" `
-Memory $false `
-Quiesce $false
Bulk snapshot across a VM folder
# -RunAsync prevents blocking the loop while each snapshot task completes
# Without it, PowerCLI waits for each VM sequentially — slow across 20+ VMs
Get-VM -Location "Production" | ForEach-Object {
Write-Host "Snapping $($_.Name)..."
New-Snapshot -VM $_ `
-Name "Before patch 2025-03" `
-Description "March maintenance window" `
-Memory $false `
-Quiesce $false `
-RunAsync
}
What you will see in the console when this runs: For each VM, PowerCLI prints the status line from Write-Host, then immediately outputs a task object table — one row per VM. The script will finish in seconds regardless of how many VMs are in the location.
Snapping SRV-APP-01...
Id Name State StartTime FinishTime Result
-- ---- ----- --------- ---------- ------
Task-task-4821 CreateSnapshot_Task Running 10/05/2026 08:14:32
Snapping SRV-DB-01...
Id Name State StartTime FinishTime Result
-- ---- ----- --------- ---------- ------
Task-task-4822 CreateSnapshot_Task Running 10/05/2026 08:14:33
Get-Task | Where-Object {$_.Name -eq "CreateSnapshot_Task"} or check the vSphere Client task list.
Bulk snapshot with per-VM error handling
# Wrapping each VM in try/catch ensures one failure does not abort the entire loop
# $results accumulates status per VM for review after the run
$vms = Get-VM -Location "Production"
$results = @()
foreach ($vm in $vms) {
try {
New-Snapshot -VM $vm `
-Name "Before patch 2025-03" `
-Memory $false `
-Quiesce $false `
-Confirm:$false | Out-Null
$results += [PSCustomObject]@{
VM = $vm.Name
Status = "OK"
Error = ""
}
Write-Host "OK: $($vm.Name)"
} catch {
$results += [PSCustomObject]@{
VM = $vm.Name
Status = "FAILED"
Error = $_.Exception.Message
}
Write-Warning "FAILED: $($vm.Name) — $($_.Exception.Message)"
}
}
# Export full result to CSV for review
$results | Export-Csv -Path "C:\logs\snapshot-create-results.csv" -NoTypeInformation
Write-Host "Done. Results saved to C:\logs\snapshot-create-results.csv"
What you will see: The script runs sequentially — one VM at a time, waiting for each snapshot before moving to the next. Console output is one line per VM: OK: SRV-APP-01 or FAILED: SRV-DB-01 — <error message>. When finished, the CSV at C:\logs\snapshot-create-results.csv contains a full per-VM status record. This is slower than the -RunAsync approach but gives you immediate visibility into failures without needing to check vCenter separately.
Get-VM | Get-Snapshot to confirm which VMs were snapped successfully.
Step 3 — List and audit snapshots
Use Get-Snapshot to inspect what snapshots exist. The basic form returns all snapshots for a VM; piping through Select-Object adds useful context for audit and cleanup decisions.
List snapshots for a specific VM
# Returns Name, Description, Created date, SizeGB, and parent snapshot reference
Get-Snapshot -VM "SRV-APP-01" | Select-Object Name, Description, Created, SizeGB, ParentSnapshot
Audit all snapshots across the environment
# Format-Table -AutoSize adjusts column widths to content — useful in wide terminals
Get-VM | Get-Snapshot | Select-Object VM, Name, Description, Created, SizeGB | Format-Table -AutoSize
Find unexpectedly large snapshots
# Snapshots exceeding 10 GB have been growing for a while or were never removed
# SizeGB reflects delta growth since creation — not the original VM disk size
Get-VM | Get-Snapshot | Where-Object { $_.SizeGB -gt 10 } |
Select-Object VM, Name, Created, SizeGB |
Sort-Object SizeGB -Descending |
Format-Table -AutoSize
Inspect the snapshot tree (parent-child chains)
vSphere snapshots form a tree, not a flat list. A VM may have multiple branches — for example, snapshots taken at different stages of a migration. Inspecting the parent chain before reverting or removing avoids operating on the wrong node.
# Walk the snapshot chain for a VM and show parent-child relationships
Get-Snapshot -VM "SRV-APP-01" | ForEach-Object {
[PSCustomObject]@{
Name = $_.Name
Created = $_.Created
SizeGB = [math]::Round($_.SizeGB, 2)
ParentSnapshot = if ($_.ParentSnapshot) { $_.ParentSnapshot.Name } else { "(root)" }
}
} | Format-Table -AutoSize
Step 4 — Revert to a snapshot
If a patch or change causes problems, revert the VM to its pre-change state. Identify the exact snapshot first — especially on VMs with chained snapshots — before executing the revert.
Basic revert
# Store the snapshot in a variable first to verify you have the right one
$snap = Get-Snapshot -VM "SRV-APP-01" -Name "Before patch 2025-03"
# Confirm the snapshot details before reverting
Write-Host "Reverting to: $($snap.Name) — created $($snap.Created)"
Set-VM -VM "SRV-APP-01" -Snapshot $snap -Confirm:$false
Revert with power state management
# Check VM power state before reverting — avoids abrupt power-off on running VMs
# Shut down cleanly through VMware Tools if the VM is powered on
$vm = Get-VM -Name "SRV-APP-01"
$snap = Get-Snapshot -VM $vm -Name "Before patch 2025-03"
if ($vm.PowerState -eq "PoweredOn") {
Write-Host "Shutting down $($vm.Name) via VMware Tools..."
Shutdown-VMGuest -VM $vm -Confirm:$false | Out-Null
# Wait for the VM to power off before issuing the revert
while ((Get-VM -Name $vm.Name).PowerState -ne "PoweredOff") {
Start-Sleep -Seconds 5
Write-Host "Waiting for power off..."
}
}
Write-Host "Reverting $($vm.Name) to snapshot: $($snap.Name)"
Set-VM -VM $vm -Snapshot $snap -Confirm:$false
Step 5 — Remove a snapshot
Once a change is confirmed successful, remove the snapshot to reclaim datastore space. Removal commits the delta changes into the parent disk — this can take significant time and generates I/O load on the datastore during consolidation.
Remove a specific snapshot
# Target the snapshot by name to avoid accidentally removing the wrong one
$snap = Get-Snapshot -VM "SRV-APP-01" -Name "Before patch 2025-03"
Remove-Snapshot -Snapshot $snap -Confirm:$false
Remove all snapshots for a VM
# Removes the entire snapshot tree — all branches and all children
# -RemoveChildren is implied when removing the root; use with caution on VMs with multiple branches
Get-Snapshot -VM "SRV-APP-01" | Remove-Snapshot -RemoveChildren -Confirm:$false
Step 6 — Check for consolidation needed
A snapshot can be removed through the UI or PowerCLI but the underlying disk files may not consolidate if the task fails silently. vCenter tracks this state in Runtime.ConsolidationNeeded. VMs in this state continue to consume delta disk space even though the snapshot no longer appears in the snapshot manager.
# Find all VMs where disk consolidation is needed
Get-VM | Where-Object { $_.ExtensionData.Runtime.ConsolidationNeeded -eq $true } |
Select-Object Name, PowerState | Format-Table -AutoSize
# Trigger consolidation on a specific VM
$vm = Get-VM -Name "SRV-APP-01"
$vm.ExtensionData.ConsolidateVMDisks_Task()
ConsolidationNeeded is also surfaced as a warning badge in the vSphere Client under the VM summary tab. Checking it via PowerCLI lets you detect and remediate it across your full estate in one pass, rather than spotting individual VMs in the UI.
Step 7 — Find and clean up stale snapshots
Stale snapshot detection is a standard maintenance task. The script below runs a full audit, exports a CSV report, and optionally removes everything beyond the age threshold — with a dry-run mode so you can review before deleting.
<#
.SYNOPSIS
Audit and optionally remove snapshots older than a specified threshold.
.PARAMETER DaysOld
Age threshold in days. Snapshots older than this are flagged for removal.
.PARAMETER DryRun
When $true, reports findings without removing anything. Set to $false to delete.
.PARAMETER ReportPath
Path for the CSV output. Defaults to C:\logs\stale-snapshots.csv.
#>
param(
[int]$DaysOld = 7,
[bool]$DryRun = $true,
[string]$ReportPath = "C:\logs\stale-snapshots.csv"
)
$threshold = (Get-Date).AddDays(-$DaysOld)
Write-Host "Scanning for snapshots older than $DaysOld days (before $threshold)..."
# Collect stale snapshots — $_.Created is the timestamp from vCenter
$stale = Get-VM | Get-Snapshot | Where-Object { $_.Created -lt $threshold }
if ($stale.Count -eq 0) {
Write-Host "No stale snapshots found."
exit
}
# Build report
$report = $stale | ForEach-Object {
[PSCustomObject]@{
VM = $_.VM.Name
Snapshot = $_.Name
Description = $_.Description
Created = $_.Created
AgeDays = [math]::Round(((Get-Date) - $_.Created).TotalDays, 1)
SizeGB = [math]::Round($_.SizeGB, 2)
Action = if ($DryRun) { "Would remove" } else { "Removed" }
}
}
$report | Format-Table -AutoSize
$report | Export-Csv -Path $ReportPath -NoTypeInformation
Write-Host "Report saved to $ReportPath"
if ($DryRun) {
Write-Host "`nDry run mode — no snapshots were removed. Re-run with -DryRun `$false to delete."
} else {
Write-Host "`nRemoving $($stale.Count) snapshots..."
foreach ($snap in $stale) {
try {
Remove-Snapshot -Snapshot $snap -Confirm:$false
Write-Host "Removed: $($snap.VM.Name) — $($snap.Name)"
} catch {
Write-Warning "Failed to remove $($snap.VM.Name) — $($snap.Name): $($_.Exception.Message)"
}
}
Write-Host "Done."
}
Usage examples
-DryRun $false. Pay particular attention to VMs with chained snapshots — removing a parent deletes all child snapshots in the chain as well.
# Dry run — see what would be removed without touching anything
.\Remove-StaleSnapshots.ps1 -DaysOld 7 -DryRun $true
# Live run — remove all snapshots older than 14 days and export CSV
.\Remove-StaleSnapshots.ps1 -DaysOld 14 -DryRun $false -ReportPath "C:\logs\cleanup-2025-03.csv"
What you will see during a live run: The script first prints the stale snapshot table to the console, saves the CSV, then iterates through each snapshot printing Removed: SRV-APP-01 — Before patch 2025-03 per deletion. Failures appear as WARNING: lines and do not stop the loop. After the script exits, run the consolidation check from Step 6 to confirm all delta files were cleared.
Hidden gems
ParentSnapshot is null at the root — check before walking the chain
When iterating through a snapshot tree, $snap.ParentSnapshot returns $null for the root snapshot. If your script tries to access properties of the parent without a null check, it will throw. Always guard with if ($snap.ParentSnapshot) before referencing it.
Get-Snapshot -Id is faster than -Name in large environments
When you already know the snapshot ID (from a prior run or a stored report), querying by -Id avoids iterating through the full snapshot tree of the VM. Useful in scripts that store snapshot IDs after creation for later targeted removal.
# Store the ID at creation time
$snap = New-Snapshot -VM "SRV-APP-01" -Name "Before patch 2025-03" -Memory $false -Quiesce $false
$snapId = $snap.Id
# Retrieve the exact snapshot later without name matching
Get-Snapshot -VM "SRV-APP-01" -Id $snapId
SizeGB reflects delta growth, not total VM disk size
SizeGB on a snapshot object is the size of the delta file accumulated since snapshot creation — not the full VM disk size. A snapshot showing 0.1 GB immediately after creation will grow to reflect every write the VM makes afterward. Two snapshots from the same VM taken hours apart can show very different sizes based solely on write activity between them.
-RunAsync and task monitoring
# After submitting async snapshot tasks, monitor until all complete
# Without this, the next step in a pipeline may run before snapshots are ready
$tasks = Get-VM -Location "Production" | New-Snapshot -Name "Before patch 2025-03" -Memory $false -Quiesce $false -RunAsync
# Wait for all submitted tasks to finish
Wait-Task -Task $tasks
Write-Host "All snapshot tasks completed."
Tips and limitations
- Snapshots are not backups. They depend on the base VMDK remaining intact. If the datastore fails, the snapshot chain is unrecoverable along with it.
- Memory snapshots are expensive.
-Memory $truecaptures the full RAM state to a.vmemfile, which can be several gigabytes. Only use it when you need to resume the VM from an exact running state — for patch rollbacks, disk-only snapshots are sufficient and faster. - Quiesce requires VMware Tools.
-Quiesce $truewill fail silently or throw if VMware Tools is not running inside the guest. Always verify Tools status on the VM before enabling quiescing for database VMs. - Snapshot chains degrade VM performance. Each write from the VM goes to the delta file. With three or more snapshots in a chain, disk I/O is meaningfully slower. VMware recommends no more than 2–3 snapshots in a chain at any time.
- Removal is slow on large deltas. Consolidating a 30 GB delta file can take 20–40 minutes on a typical SAN-backed datastore. In environments with many VMs, stagger removals across maintenance windows rather than running bulk removal during peak hours.
- ConsolidationNeeded can go unnoticed. If a previous snapshot removal task failed mid-way, the snapshot disappears from the UI but the delta files remain. Always run the consolidation check after any bulk removal operation.
Step 8 — Disconnect when done
Disconnect-VIServer -Server * -Confirm:$false
Official documentation
Related tools
- PowerCLI Command Builder – quickly create simple PowerCLI commands
Related guides
- How to install VMware PowerCLI and connect to vSphere — required setup before any snapshot operation covered in this article
- Getting started with PowerCLI automation scripts — extends the bulk snapshot patterns shown here into reusable script modules
- VMware PowerCLI command reference — full cmdlet index for cross-referencing parameters used in this guide