Virtu-Al.Net

Virtually everything is poshable

PowerCLI: SnapReminder

Fed up of chasing those people who constantly create snapshots and leave them hanging around for weeks or even months on end ?

You no longer have to do the chasing, just use the following script to automatically find the offending snapshot, find the person who created it, get their email address from AD and send them an email reminding them of their mortal sin.

The email address is taken from  the Email field as shown below:

SnapReminder_AD

A few requirements:

  • The accounts must have the E-Mail field filled out
  • The account you run the script as must have read permissions to AD (Any member of the domain should have this)
  • You need to fill in the smtp server address, VI Server name and the from address at the top of this script
  • You can run this as a scheduled task to constantly email the offending parties :)
  • The below script is set to remind of anything over 2 weeks old but this can easily be amended
    Once completed the offending person will receive an email as below:SnapReminder

    Thanks to LucD for helping with finding the user who created the snapshot on the PowerCLI Forum.

The Script:

# - SnapReminder V1.0 By Virtu-Al - http://virtu-al.net
#
# Please use the below variables to define your settings before use
#
$smtpServer = "mysmtpserver.mydomain.com"
$MailFrom = "me@mydomain.com"
$VISRV = "MYVISERVER"

function Find-User ($username){
	if ($username -ne $null)
	{
		$usr = (($username.split("\"))[1])
		$root = [ADSI]""
		$filter = ("(&(objectCategory=user)(samAccountName=$Usr))")
		$ds = new-object system.DirectoryServices.DirectorySearcher($root,$filter)
		$ds.PageSize = 1000
		$ds.FindOne()
	}
}

function Get-SnapshotTree{
	param($tree, $target)

	$found = $null
	foreach($elem in $tree){
		if($elem.Snapshot.Value -eq $target.Value){
			$found = $elem
			continue
		}
	}
	if($found -eq $null -and $elem.ChildSnapshotList -ne $null){
		$found = Get-SnapshotTree $elem.ChildSnapshotList $target
	}

	return $found
}

function Get-SnapshotExtra ($snap){
	$guestName = $snap.VM	# The name of the guest

	$tasknumber = 999		# Windowsize of the Task collector

	$taskMgr = Get-View TaskManager

	# Create hash table. Each entry is a create snapshot task
	$report = @{}

	$filter = New-Object VMware.Vim.TaskFilterSpec
	$filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime
	$filter.Time.beginTime = (($snap.Created).AddSeconds(-5))
	$filter.Time.timeType = "startedTime"

	$collectionImpl = Get-View ($taskMgr.CreateCollectorForTasks($filter))

	$dummy = $collectionImpl.RewindCollector
	$collection = $collectionImpl.ReadNextTasks($tasknumber)
	while($collection -ne $null){
		$collection | where {$_.DescriptionId -eq "VirtualMachine.createSnapshot" -and $_.State -eq "success" -and $_.EntityName -eq $guestName} | %{
			$row = New-Object PsObject
			$row | Add-Member -MemberType NoteProperty -Name User -Value $_.Reason.UserName
			$vm = Get-View $_.Entity
			$snapshot = Get-SnapshotTree $vm.Snapshot.RootSnapshotList $_.Result
			$key = $_.EntityName + "&" + ($snapshot.CreateTime.ToString())
			$report[$key] = $row
		}
		$collection = $collectionImpl.ReadNextTasks($tasknumber)
	}
	$collectionImpl.DestroyCollector()

	# Get the guest's snapshots and add the user
	$snapshotsExtra = $snap | % {
		$key = $_.vm.Name + "&" + ($_.Created.ToString())
		if($report.ContainsKey($key)){
			$_ | Add-Member -MemberType NoteProperty -Name Creator -Value $report[$key].User
		}
		$_
	}
	$snapshotsExtra
}

Function SnapMail ($Mailto, $snapshot)
{
	$msg = new-object Net.Mail.MailMessage
	$smtp = new-object Net.Mail.SmtpClient($smtpServer)
	$msg.From = $MailFrom
	$msg.To.Add($Mailto)

	$msg.Subject = "Snapshot Reminder"

$MailText = @"
This is a reminder that you have a snapshot active on $($snapshot.VM) which was taken on $($snapshot.Created).

Name: $($snapshot.Name)

Description: $($snapshot.Description)
"@	

	$msg.Body = $MailText
	$smtp.Send($msg)
}

Connect-VIServer $VISRV

foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-14))})){
	$SnapshotInfo = Get-SnapshotExtra $snap
	$mailto = ((Find-User $SnapshotInfo.Creator).Properties.mail)
	SnapMail $mailto $SnapshotInfo
}

, ,

47 Responses to “PowerCLI: SnapReminder”

  • Ivo Beerens says:

    Hey Allan, a suggestion it would be nice to see the size of the snapshot file(s) in GB the e-mail

  • Virtu-Al says:

    @Ivo Beerens
    Good thinking V2 Coming up !

  • Virtu-Al says:

    @Jason Boche
    Thanks man

  • [...] Snapshots have always been a powerful feature of VMware ESX — even in production deployments.  There are tons of innovative tools and scripts out there to monitor snapshots — just check out this new one from Virtu-Al. [...]

  • [...] Virtu-Al » PowerCLI: SnapReminder. [...]

  • chris says:

    I get an error when running the script error line 93 char 13

    + $MailText = @ <<<< "

    If I remove the @ I get another error with another @ symbol farther down.

  • Virtu-Al says:

    @chris
    My blog mangled the code, I have now moved it over to a different hosting method so please try the above.

    Let me know if you have any further issues.

  • Daniel says:

    Good script,thanks. I had a problem with the timing, so I changed the line 50 to:
    $filter.Time.beginTime = (($snap.Created).AddMinutes(-30)

  • Falko says:

    Well, for me it is semi-working. I get emails, but the script gives me the following error:

    You cannot call a method on a null-valued expression.
    At :line:63 char:83
    + $key = $_.EntityName + “&” + ($snapshot.CreateTime.ToString <<<< ())

    Index operation failed; the array index evaluated to null.
    At :line:64 char:32
    + $report[ <<<< $key] = $row

  • Daniel says:

    @Falko
    I had this error also. In my case it was because the Administrator made the snapshot an there is no email adress for Administrator in the AD. My Solution:

    Line 10:
    if ($username -ne $null -and $username -ne “Administrator”)

    Line 86:
    if($Mailto -eq $null){
    $msg.To.Add(“vmadmin@blah.bla”)
    }
    else{
    $msg.To.Add($Mailto)
    }

    Hth

  • [...] Man kann via vCenter Alarme für Snapshots definieren (etwas mühselig), via Powershell-Skript die Ersteller von alten Snapshots automatisiert benachrichtigen, den snapshotmanager.pl des Perl SDKs zum Anzeigen verwenden oder aber den Report von William [...]

  • [...] and most used one I use is the SnapReminder script by scripting guru Alan Renouf. Alan’s script is a fairly straight forward one (his words not mine [...]

  • [...] and the most used one I use is the SnapReminder script by scripting guru Alan Renouf. Alan’s script is a fairly straight forward one (his words not mine [...]

  • [...] and the one i use the most is the SnapReminder script by scripting guru Alan Renouf. Alan’s script is a fairly straight forward one (his words not mine [...]

  • [...] and the one i use the most is the SnapReminder script by scripting guru Alan Renouf. Alan’s script is a fairly straight forward one (his words not mine [...]

  • Jeramey says:

    Where do you stand on a v2 of the snapshot reminder?

  • [...] Alan Renouf’s Snapshot Reminder – A Powershell script that integrates with AD to send the creator of a snapshot a little reminder when the snapshot is over 2 weeks old. [...]

  • Excellant write-up! Thanks for the tip, works like a charm! Nagging has begun! :)

  • Mr G says:

    Excellent script and works very well! Is there anyway to have this script focus on a specific datacenter in vCenter?

  • Virtu-Al says:

    Mr G

    Sure, just change line 104 to the following:

    foreach ($snap in (Get-Datacenter “MyDC” | Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-14))})){

  • Rudz says:

    Can someone tell me where i can find the V2 of this excellent Script ?? :)

  • [...] admin, thinking a snapshot is also a backup. Thankfully, Alan Renouf over at virtu-al.net wrote the SnapReminder, which already helped me a lot! However, occasionally the script isn’t finding the snapshot [...]

  • Pat Allen says:

    Hi Al,

    I’ve run vCheck on a daily basis for quite a while now and it’s GREAT! Thanks for putting this and everything else out in the community.

    I decided to put together a script to automate reminding folks about snapshots. My first idea was parsing the output of vCheck but then I checked your site and found SnapRemind! This is EXACTLY what I need to put together and it would save me a tremendous amount of work. (Especially considering that I’m an old Unix nerd and I’m just starting to learn Power CLI.) But I’m having a little problem with things. When I run your vCheck script, it shows me all the snapshots with the correct creator names. When I run the SnapReminder script, it always shows the name of the local administrator as the creator of the snapshot. The local admin doesn’t have an email address so no mail is sent. I’m running vSphere 4.1 in case that matters.

    Any idea what might be wrong?

    THANKS again!
    Pat

  • Virtu-Al says:

    That is strange, what happens if you replace the bottom foreach loop with this:

    foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-14))})){
    Get-SnapshotExtra $snap
    }

    Do you get the creator for each snapshot listed ?

  • Jill says:

    This script doesn’t work if the snap was created with a scheduled task – there is no email recipient. Is there a way around this?

  • Pat Allen says:

    Sorry I didn’t get to this for a few days. I changed the for loop and am getting the same result. One thing that I notice is that I get lots (LOTS) of errors while running the script like this:

    You cannot call a method on a null-valued expression.
    At C:\Users\pat\Desktop\SnapReminder.ps1:63 char:63
    + $key = $_.EntityName + “&” + ($snapshot.CreateTime.ToString <<<< ())
    + CategoryInfo : InvalidOperation: (ToString:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

    Daniel's post from 2009 indicates that this is because the administrator created the snapshot which isn't the case in my situation although the script says that the local administrator ("meteor") did create it.

    Anyway, I'm running it against my test lab cluster where there are 3 snapshots. Here's an example of the information from one of the snapshots:

    Creator : meteor
    Description :
    Created : 11/29/2010 2:30:44 PM
    Quiesced : False
    PowerState : PoweredOn
    VM : centrify3
    VMId : VirtualMachine-vm-3883
    Parent :
    ParentSnapshotId :
    Children :
    SizeMB : 7721.44
    IsCurrent : True
    IsReplaySupported : False
    Id : VirtualMachineSnapshot-snapshot-3886
    Name : before centrify install

    Here's the information on the same snapshot taken from a daily vCheck report generated this morning at 3AM:
    centrify3 before centrify install 51 Allen, Pat 8714.44 11/29/2010 2:30:44 PM
    It shows the correct creator, "Allen, Pat". It's interesting to note that the size is different. At 3AM it was 8714MB whereas at 2PM it's 7721MB.

    Any thoughts would certainly be appreciated! THANKS!
    Pat

  • Frank says:

    Is it also possible to add the users fullname. So you can personalize the mail.

    Hi $fullname

    blablabla

    I tried to alter the script but i’m a complete noob without people I know with the rigt knowledge.

  • [...] for example my SnapReminder script, the fact that you can pull VMware Information from your vCenter, then run the results by Active [...]

  • [...] This script uses the Quest AD cmdlets to resolve the username in AD, if you don't have these installed then you can either install them or use the Microsoft AD cmdelts or I have a small function which will do the same thing, I previously used this here. [...]

  • Marcin Frycz says:

    Hi Frank,

    I have done this like that:

    $MailText = @”
    This is a reminder that you have a snapshot active on $($snapshot.VM) which was taken on $($snapshot.Created).

    Created by: $($(Find-User $SnapshotInfo.Creator).Properties.name)
    Email: $($(Find-User $SnapshotInfo.Creator).Properties.mail)

    Name: $($snapshot.Name)
    Description: $($snapshot.Description)
    Snapshot size: $([math]::Round($snapshot.SizeMB))MB
    “@

    $msg.Body = $MailText
    $smtp.Send($msg)
    }

    So you will have nice presentation of who/when/why/size and you probably can put whatever info youd like to :) just need to add few lines…

    My question on the other hand is:

    how could I create only one email with all of the snapshots taken?

    i am thinking on putting a loop on the end of the script but not sure where…

  • Marcin Frycz says:

    I believe that it could be done through some sort of css and html formatting as well

  • Marcin Frycz says:

    All fixed…

    so not to worry anymore ;)

  • Paul Lawrence says:

    How can I tell this script to get all snapshots regardless. I tried changing the line

    foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-14))

    to

    foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(0))

    but I get a NULL value error.
    Thanks

  • Andreea says:

    To login to vC we are using OU accounts and we can’t add the email to the E-Mail field because it’s already in use by our regular accounts. Is there any way I can put the email under Web Page filed and point to that field for the emails to be sent?
    Thanks

  • OnlyMe says:

    @Andreea -

    Should be – just change this line:
    $mailto = ((Find-User $SnapshotInfo.Creator).Properties.mail)

    Where it refers to .mail, find out what the Web Page field is called and use it here.

  • kenny says:

    How can I have the following script omit removing snaps from certain vm’s? I want to remove majority of snapshots from vm’s, but not all of my vm’s but this would be very tedious to do by hand!

    script -

    $vcserver=”xxxxxx”

    Add-PSsnapin VMware.VimAutomation.Core
    #Initialize-VIToolkitEnvironment.ps1
    connect-VIServer $vcserver

    get-vm | get-snapshot | where { $_.name -eq “xxxxxxxxxx” } | remove-snapshot -confirm:$false

  • Chris says:

    Can anyone help me. I keep getting the same error running this script. It seems like it can’t get the username.

    Exception calling “Add” with “1″ argument(s): “Value cannot be null.
    Parameter name: item”
    At C:\Users\gateway\Desktop\snap.ps1:86 char:13
    + $msg.To.Add <<<< ($Mailto)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

    Exception calling "Send" with "1" argument(s): "A recipient must be specified."
    At C:\Users\gateway\Desktop\snap.ps1:99 char:12
    + $smtp.Send <<<< ($msg)
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

  • Jurgen says:

    Chris

    Remove the following line:

    $key = $_.vm.Name + “&” + ($_.Created.ToString())

    That works for me because i’m in a not UCT/GMT timezone

  • Jurgen says:

    Or change the following rule:

    $key = $_.EntityName + “&” + ($snapshot.CreateTime.ToString())

    To:

    $timedeviation = get-date -uformat “%Z”
    $key = $_.EntityName + “&” + (($snapshot.CreateTime.AddHours($timedeviation)).ToString())

  • NN says:

    I tried the script on 4.1 and it gets all the snapshots (and sizes with a small modification) but the creator can not be found for ANY of the dozen or so snapshots. Any ideas why? The snapshots have been created using vSphere client, either while on ESX 4.0 or on ESXi 4.1 – no difference in results. I tried to dump the whole SnapshotInfo-object, but couldn’t find anything pointing to the creator of the snapshot.

  • Virtu-Al says:

    What do you get if you change the last loop to this, do you get a username ?

    foreach ($snap in (Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-14))})){
       $SnapshotInfo = Get-SnapshotExtra $snap
    Write-Host "Snapshot $($snap.name) has a creator of $($SnapshotInfo.Creator)."
    }
  • NN says:

    Thanks for the swift response. Unfortunately the output is (for each snapshot) “Snapshot ***** has a creator of .”
    I can see the use that initiated the taks in vSphere client, so the info has to be somewhere. I also already tried that timedeviation-trick that Jurgen suggested, although I’m not quite sure why it would help since all the servers are on the same timezone.

  • Bryan says:

    Whew, I’m not alone!

    I too have spent the day trying to get SnapReminder able to email the creator of the snapshot. It seems that there is not a “Creator” attribute (value? variable?). When I run the script through the PowerGUI Script Editor, I can expand the$SnapshotInfo variable, I do not see “Creator” – or any reference to the creator of the snapshot.

    I’ve found a script that pulls out snapshot creator information from the VPX_TASK table in the vCenter database, so the information is in there. I’m not sure where else it might recorded.

  • NN says:

    Dumped some more objects and actually it WAS what Jurgen suggested. Probably works nicely in UK as-is, but if the timezone isn’t GMT/UTC you’re generating two different $key’s in the code.
    This resolved some of the problems, but still _old_ (over 2yrs or so) snapshots are missing the creator – for which I can see two plausible reasons – too many tasks and/or this damn DST (making the deviation an ever-changing target).
    But this is a minor issue, since who would want daily e-mails for lengthy periods of time ;)
    Thanks for the script anyway.

  • Martin says:

    This is a great script you can run on a daily scheduled basis. Just modify the following statement in the ‘foreach’ loop towards the bottom of the script: “(Get-Date).AddDays(0)”. Awesome script and works as described. Kudos to the author!

  • Steve says:

    I just can’t get this script to work it just keeps saying that it doesn’t expect $smptServer at he beginning.nplease please help

    Thanks Steve

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>