This is one of those management tasks that comes up at any location you’re at, especially when you’re trying to manage VMs, performance or datastore space. The cleanup task of deleting snapshots is easy, but the questions that always comes to mind are: Who created it, when and why. Take a look at a quick script I wrote to answer that information. I schedule it to run every Monday morning and email the results. Simple! This framework works for both VMware and Hyper-V.

Snapshot Management

As I mentioned above, it’s easy to delete the snapshot, but why was it created. It would be great if everyone put in meaningful names, a descriptive description, and also told us when we could delete the snapshot. The longer we leave snapshots, the more we are going to degrade performance, not only to the VM itself, but as the snapshot grows, it will take extra cycles away from the hosts to serve up the required data.

Snapshots should really only be used as a Cover Your A** (CYA). Essentially:

  1. Take a snapshot
  2. Make a change to the VM (Upgrade, Patch)
  3. Test the change
  4. Make a choice
    1. Changes are good, delete the snapshot
    2. Changes failed, revert and delete the snapshot

Reality – I’ll just keep the snapshot for a few days in case someone finds an issue.

Password Issues

Ever keep that snapshot for over 30 days? Well if you revert a Windows machine to snapshot that is older than 30 days, you’re most likely going to have machine password authentication problems with the domain. By default, machine account password changes are initiated by the computer every 30 days. So that means when you go to log into the machine, the trust between the machine and AD is broken.

To get around this issue, you can try the following options:

  1. Log on with a local account
  2. Disconnect the network adapter, log on with a domain account with cached credentials

Now if there are legitimate reasons for retaining snapshots for a length of time (Packaging machines, Gold Images etc), you may want to look at the following security option (via local security editor or group policy)

Setting: Domain member: Maximum machine account password age

Location: Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options

Report Output

Here is a sample of what the PowerShell script will create.

The Code (VMware Example)

The code itself for reporting is pretty simple and short. The script I have is larger cause I try and write all my useful scripts with script parameters, and in this case the code is a little larger as I kick it out to email.


The parameters are pretty straight forward. Which virtual center machine are we going to connect to, how to send an email, and most importantly, only email machines that are older than X days (14 by default).


param (
    $VirtualCenter = "VirtualCenter.corp.local",
    $smtpServer = "smtp1.corp.local",
    $smtpFrom = "vmware@corp.local",
    $smtpTo = "arafuse@corp.local",
    $smtpSubject = "VMware Snapshots",
    $SnapShotsOlderThanXDays = 14

Connect to Virtual Center

Next step is to connect into Virtual Center

Get-Module -ListAvailable VMware.VimAutomation.* | Import-Module -ErrorAction SilentlyContinue
If ($global:DefaultVIServer) {
    Disconnect-VIServer * -Confirm:$false -ErrorAction SilentlyContinue
$VCServer = Connect-VIServer -Server $VirtualCenter


Get and Create the Snapshot Report

Here is the worker code of this script. Some VMs are allowed to have snapshots, so we define a list of regular expressions to filter out. The next one is some of the secret sauce to figuring out who created the snapshot. To do this we need to go back through the VM events and look for the Create Snapshot even. From here we can determine who created the snapshot. To help limit the speed, we know what time the snapshot was created, so this code is going to look at the past 4000 events for that VM starting 10 seconds before the snapshot was created.

As I sometimes run this code interactively, I first create a report with all the snapshots regardless of the date created (excluding the allowed VM with snapshots). This allows me to see everything. But during script execution, I then filter out anything older than 14 days. Those are the culprits I want to delete!

$VmsWithAllowedSnaps = @(".*SnappyImage.*")
$LogEntriesPerVM = 4000

$VMs = Get-VM
Foreach ($VmsWithAllowedSnap in $VmsWithAllowedSnaps) {
    $VMs = $VMs | Where {$_.Name -notmatch $VmsWithAllowedSnap}
$SnapShots = $VMs | Get-Snapshot

$date = Get-Date
$measure = Measure-Command { 
    $report = $Snapshots | Select-Object VM, Name, @{Name="User"; Expression = { (Get-VIEvent -Entity $_.VM -MaxSamples $LogEntriesPerVM -Start $_.Created.AddSeconds(-10) | Where {$_.Info.DescriptionId -eq "VirtualMachine.createSnapshot"} | Sort-Object CreatedTime | Select-Object -First 1).UserName}}, Created, @{Name="Days Old"; E={$_.Created - }}, Description | Sort-Object -Property "Created" 

$report = $report | Where {($_.Created).AddDays([int]$SnapShotsOlderThanXDays) -lt (Get-Date)} 


Email the Results

Scripts are great! Scripts that email you the results are even greater! You can use this generic fragment of code almost anywhere. It takes your $report object, put it into an HTML table and emails it! If you don’t like the colors, there are many things you can do in the $head block below by adding/modifying CSS styles.


$head = @"
<title>Snapshot Daily/Weekly Report</title>
<style type="text/css">
  body { background-color: white; } 
  table { border-width: 1px; border-style: solid; border-color: black; border-collapse: collapse; }
  th {border-width: 1px; padding: 0px; border-style: solid; border-color: black; background-color:thistle }
  td {border-width: 1px ;padding: 0px; border-style: solid; border-color: black; }
  tr:nth-child(odd) { background-color:#d3d3d3; } 
  tr:nth-child(even) { background-color:white; }

$postContent = @"
<p>Number of Snapshots: $($report.count)</p>
<p>Generated on $($ENV:COMPUTERNAME)</p>

#Send Email Report
$date = Get-Date
$message = New-Object System.Net.Mail.MailMessage $smtpFrom, $smtpTo
$message.Subject = $smtpSubject
$message.IsBodyHTML = $true

$SnapshotReportHTML = $report | ConvertTo-Html -Head $head -PreContent "Report Date: $date" -PostContent $PostContent
$message.Body = $SnapshotReportHTML | Out-String
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)


Schedule It

You’ll see me type this over and over. I schedule this script to run on Monday mornings. This way when I come into the office, there is a report sitting in people’s mailboxes. It’s clean up time!


Happy Snapshot Management!