VM snapshots are indispensable for rollback safety — but left unchecked, they grow silently, consume datastore space, and degrade VM performance. In environments with Prod and DR vCenters, manually tracking snapshots across hundreds of VMs is error-prone and time-consuming.
This guide walks you through a fully automated solution using PowerCLI + Jenkins Parameterised Build that:
The diagram below shows how the components interact at scheduled trigger time:
Save this as Get-VMSnapshots.ps1 on your Jenkins agent. All sensitive values are injected as environment variables from Jenkins — no hardcoded credentials.
<# .SYNOPSIS VMware Snapshot Report — PowerCLI + Email Alert .DESCRIPTION Connects to a specified vCenter (Prod or DR), identifies all VMs with snapshots, exports a timestamped CSV, and emails the report. .NOTES Author : automatewithravi.com Version : 1.0 All credentials and server names are injected via Jenkins env vars. #> # ── Initialise transcript log ────────────────────────────────────────────── $Timestamp = Get-Date -Format 'MM-dd-yyyy_HHmmss' $LogPath = "C:\Jenkins_Logs\SnapshotReport_$Timestamp.txt" $CsvPath = "C:\Jenkins_Reports\VMSnapshots_$Timestamp.csv" New-Item -ItemType Directory -Force -Path "C:\Jenkins_Logs" | Out-Null New-Item -ItemType Directory -Force -Path "C:\Jenkins_Reports" | Out-Null Start-Transcript -Path $LogPath -NoClobber -IncludeInvocationHeader # ── Read credentials from Jenkins environment variables ──────────────────── $vCenter = $Env:VCENTER_SERVER # e.g. prod-vcenter.corp.local or dr-vcenter.corp.local $Username = $Env:VCENTER_USER $Password = $Env:VCENTER_PASS $SmtpServer = $Env:SMTP_SERVER # e.g. smtp.corp.local $EmailFrom = $Env:SMTP_FROM # e.g. alerts@automatewithravi.com $EmailTo = $Env:SMTP_TO # e.g. infra-team@automatewithravi.com $Environment = $Env:ENVIRONMENT # "Prod" or "DR" # ── Connect to vCenter ───────────────────────────────────────────────────── Write-Host "[INFO] Connecting to $Environment vCenter: $vCenter" try { $Connection = Connect-VIServer -Server $vCenter -User $Username -Password $Password -ErrorAction Stop } catch { Write-Error "[ERROR] Unable to connect to $Environment vCenter ($vCenter). $_" Stop-Transcript exit 1 } if (-not $Connection.IsConnected) { Write-Error "[ERROR] vCenter connection check failed." Stop-Transcript exit 1 } Write-Host "[INFO] Successfully connected to $Environment vCenter." # ── Collect snapshot data ────────────────────────────────────────────────── Write-Host "[INFO] Retrieving VM snapshots..." $Snapshots = Get-VM | Get-Snapshot | Select-Object ` VM, Name, Description, @{Label = "SizeGB"; Expression = { "{0:N2} GB" -f $_.SizeGB }}, Created, @{Label = "AgeDays"; Expression = { (New-TimeSpan -Start $_.Created -End (Get-Date)).Days }}, @{Label = "Environment"; Expression = { $Environment }} Write-Host "[INFO] Total snapshots found: $($Snapshots.Count)" # ── Export CSV ───────────────────────────────────────────────────────────── $Snapshots | Export-Csv -Path $CsvPath -NoTypeInformation -UseCulture Write-Host "[INFO] CSV exported to: $CsvPath" # ── Send email alert ─────────────────────────────────────────────────────── if ($Snapshots.Count -gt 0) { $Subject = "[$Environment] VMware Snapshot Report — $($Snapshots.Count) snapshot(s) found — $(Get-Date -Format 'dd MMM yyyy')" $Body = @" Hi Team, The automated snapshot audit has completed for the <b>$Environment</b> vCenter environment. Summary: - vCenter : $vCenter - Environment : $Environment - Total Snapshots : $($Snapshots.Count) - Report Date : $(Get-Date -Format 'dd MMMM yyyy HH:mm') Please review the attached CSV report and action any snapshots older than your retention policy. -- Automated Report via Jenkins | automatewithravi.com "@ Send-MailMessage ` -From $EmailFrom ` -To ($EmailTo -split ",") ` -Subject $Subject ` -Body $Body ` -BodyAsHtml ` -Attachments $CsvPath ` -SmtpServer $SmtpServer Write-Host "[INFO] Email alert sent to: $EmailTo" } else { Write-Host "[INFO] No snapshots found. No email sent." } # ── Disconnect and cleanup ───────────────────────────────────────────────── Disconnect-VIServer -Server $vCenter -Confirm:$false Write-Host "[INFO] Disconnected from vCenter. Job complete." Stop-Transcript
AgeDays calculated column lets your team instantly identify stale snapshots without opening vCenter. Filter the CSV for AgeDays > 7 to flag policy violations.
The build accepts parameters so one job covers both vCenters — just change the dropdown at trigger time (or let the cron set it via separate schedules).
| Parameter Name | Type | Values / Default | Description |
|---|---|---|---|
| ENVIRONMENT | Choice | Prod, DR | Selects which vCenter to target |
| VCENTER_SERVER | String | prod-vcenter.corp.local | vCenter FQDN or IP (can be pre-filled per environment) |
| VCENTER_USER | Credential (injected) | vcenter-readonly@vsphere.local | vCenter service account username |
| VCENTER_PASS | Credential (injected) | (secret) | vCenter password — stored in Jenkins Credential Store |
| SMTP_SERVER | String | smtp.corp.local | Internal SMTP relay |
| SMTP_FROM | String | alerts@automatewithravi.com | Sender address displayed in the alert email |
| SMTP_TO | String | infra-team@automatewithravi.com | Recipient(s) — comma-separated for multiple |
In your Jenkins job configuration, scroll to Build Triggers and enable Build periodically. Use standard cron syntax:
# Every day at 07:00 AM (Mon–Fri) 0 7 * * 1-5 # Every day at 07:00 AM (7 days a week) 0 7 * * * # Every Monday at 08:00 AM 0 8 * * 1 # Every 6 hours H/6 * * * *
Open the Jenkins job → Configure → scroll to Build Triggers section.
Tick the checkbox and paste your chosen cron expression into the Schedule field.
For scheduled (unattended) runs, ensure the default parameter values match the intended environment so the job doesn't prompt interactively.
Copy the job and set ENVIRONMENT=DR and VCENTER_SERVER=dr-vcenter.corp.local as defaults. Schedule at offset times (e.g. Prod at 07:00, DR at 07:30) to avoid resource contention.
The script uses PowerShell's native Send-MailMessage cmdlet — no Jenkins email plugin needed on the script side. However, configure the SMTP relay details as follows:
| Field | Value |
|---|---|
| SMTP server | smtp.corp.local (or your relay) |
| Default user e-mail suffix | @automatewithravi.com |
| Use SSL | Tick if your relay requires TLS |
| SMTP Port | 25 (unauthenticated) or 587 (TLS) |
The PowerShell script handles all email logic directly — no additional Jenkins email plugin configuration is required for the snapshot report itself.
After a successful run you'll have:
C:\Jenkins_Logs\SnapshotReport_<timestamp>.txt[Prod] VMware Snapshot Report — 14 snapshot(s) found — 08 Jun 2026"VM","Name","Description","SizeGB","Created","AgeDays","Environment" "WEB-SVR-01","Pre-Patch-May26","Before May patch","12.44 GB","05/01/2026 09:12:00","38","Prod" "DB-SVR-04","Upgrade-Snapshot","Before DB upgrade","31.07 GB","04/15/2026 14:55:00","54","Prod" "APP-SVR-02","Test-Rollback","UAT rollback point","8.92 GB","05/28/2026 17:30:00","11","Prod"
$Snapshots | Where-Object { $_.AgeDays -gt 14 } and send a high-priority alert for stale snapshots exceeding your retention policy.
You now have a production-ready, fully automated VMware snapshot reporting pipeline that runs hands-free on a schedule:
AgeDays column for instant policy compliance viewThe complete script is available on GitHub. If you have questions or want to extend this to include snapshot age-based deletion, drop a comment or reach out via the contact page.