VMware Cloud Community
alanrenouf
VMware Employee
VMware Employee
Jump to solution

Who created the Snapshot

Any idea how I can relatemy Get-Snapshot information to the Get-VIEvent information to work out who created a snapshot ?

Thanks

Alan

Blog: http://virtu-al.net Twitter: http://twitter.com/alanrenouf Co-author of the PowerCLI Book: http://powerclibook.com
Tags (1)
Reply
0 Kudos
1 Solution

Accepted Solutions
LucD
Leadership
Leadership
Jump to solution

Short question but a long answer. In the end I got there.

There were a few quirks in writing this script.

1) The Created timestamp in the Event object is not always exactly the same as the time stamp in the VirtualMachine Snapshot property.

The solution is to use the Task Entity property to get the time stamp of the snapshot.

2) The Get-ViEvent cmdlet returns a VimApi.ManagedObjectReference instead of a VMware.Vim.ManagedObjectReference.

As such this is not a big problem since I can easily convert the one MoRef to the other (unfortunately not via casting).

3) The speed of the Get-ViEvent cmdlet is not ideal compered to the Task Collector form the APIs

From 2) and 3) I decided to go for the Task Collector methods.

The flow of the script is quite simple.

First create a hash table from all tasks that created snapshots on the specific guest.

The key into the hash table is the name of the guest concatenated with the snapshot creation timestamp.

Then the script use the Get-Snapshot cmdlet to retrieve the snapshots for the guest.

From this output a key is constructed and then the key is used to look up the user in the hash table.

The User property is added to the SnapshotImpl object.

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
}

$daysBack = 3				        # How many days back from now
$guestName = <VM-name>		                # The name of the guest

$tasknumber = 999			        # Windowsize of the Task collector

#$serviceInstance = get-view ServiceInstance
$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 = (Get-Date).AddDays(-$daysBack)
$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 = Get-VM $guestName | Get-Snapshot | % {
	$key = $_.vm.Name + "&" + ($_.Created.ToString())
	if($report.ContainsKey($key)){
		$_ | Add-Member -MemberType NoteProperty -Name User -Value $report[$key].User
	}
	$_
}

$snapshotsExtra | Export-Csv "C:\SnapshotsExtra.csv" -NoTypeInformation -UseCulture

Note1: the -UseCulture parameter on the Export-Csv cmdlet is PS v2 CTP3

Note2: it shouldn't be too difficult to convert the script to handle all guests instead of just one. The name of the guest is already in the key in the hash table.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

View solution in original post

Reply
0 Kudos
76 Replies
LucD
Leadership
Leadership
Jump to solution

Short question but a long answer. In the end I got there.

There were a few quirks in writing this script.

1) The Created timestamp in the Event object is not always exactly the same as the time stamp in the VirtualMachine Snapshot property.

The solution is to use the Task Entity property to get the time stamp of the snapshot.

2) The Get-ViEvent cmdlet returns a VimApi.ManagedObjectReference instead of a VMware.Vim.ManagedObjectReference.

As such this is not a big problem since I can easily convert the one MoRef to the other (unfortunately not via casting).

3) The speed of the Get-ViEvent cmdlet is not ideal compered to the Task Collector form the APIs

From 2) and 3) I decided to go for the Task Collector methods.

The flow of the script is quite simple.

First create a hash table from all tasks that created snapshots on the specific guest.

The key into the hash table is the name of the guest concatenated with the snapshot creation timestamp.

Then the script use the Get-Snapshot cmdlet to retrieve the snapshots for the guest.

From this output a key is constructed and then the key is used to look up the user in the hash table.

The User property is added to the SnapshotImpl object.

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
}

$daysBack = 3				        # How many days back from now
$guestName = <VM-name>		                # The name of the guest

$tasknumber = 999			        # Windowsize of the Task collector

#$serviceInstance = get-view ServiceInstance
$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 = (Get-Date).AddDays(-$daysBack)
$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 = Get-VM $guestName | Get-Snapshot | % {
	$key = $_.vm.Name + "&" + ($_.Created.ToString())
	if($report.ContainsKey($key)){
		$_ | Add-Member -MemberType NoteProperty -Name User -Value $report[$key].User
	}
	$_
}

$snapshotsExtra | Export-Csv "C:\SnapshotsExtra.csv" -NoTypeInformation -UseCulture

Note1: the -UseCulture parameter on the Export-Csv cmdlet is PS v2 CTP3

Note2: it shouldn't be too difficult to convert the script to handle all guests instead of just one. The name of the guest is already in the key in the hash table.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
alanrenouf
VMware Employee
VMware Employee
Jump to solution

Luc, is there nothing you can not do ?!

Thanks very much, I managed to adapt your code to allow me to create a function for this info.


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){
	#$daysBack = 5			# How many days back from now
	$guestName = $snap.VM	# The name of the guest
	

	$tasknumber = 999		# Windowsize of the Task collector
	
	#$serviceInstance = get-view ServiceInstance
	$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
}

$Snapshots = Get-VM | Get-Snapshot | Where {$_.Created -lt ((Get-Date).AddDays(-14))} 

$mySnaps = @()
foreach ($snap in $Snapshots){
	$SnapshotInfo = Get-SnapshotExtra $snap
	$mySnaps += $SnapshotInfo
}

$mySnaps | Select VM, Name, Creator, Description


If you found this information useful, please consider awarding points for Correct or Helpful.

Alan Renouf

http://virtu-al.net

Blog: http://virtu-al.net Twitter: http://twitter.com/alanrenouf Co-author of the PowerCLI Book: http://powerclibook.com
Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

It's the combined strength of PowerShell, PowerCLI and the SDK that allows me to do this kind of stuff.

They are the instruments, I'm just the conductor Smiley Wink

Nice adaption to a general function btw.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
cchipman
Contributor
Contributor
Jump to solution

Ok, so I have a question.  I know that this is a bit of an old topic, but why create a filter for the TaskCollector, and then not specify all the relevant filters.  I.e. you specify start time, but then don't filter on state, entity, or end time.  The only reason I bring it up is that I was running this in the snapreminder.ps1 script, and a couple of very old snapshots brought huge performance problems by bringing up hundreds of records since then. 

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Basically I did that for performance reasons, the more complex the filter, the longer it will take.

But you are of course right.

  • We should have used the TaskState -eq "success". No point in retrieving failed snapshot tasks
  • Entity is handy when you want to limit the filter to a specific branch (datacenter, folder, cluster, host...) in your vSphere environment. With the RecursionOption you can easily state for which entities you want to retrieve the tasks

In the mean time the performance of the Get-VIEvent cmdlet has improved. So you could even consider using the cmdlet instead of the collector. Just filter on TaskEvent.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
avlieshout
VMware Employee
VMware Employee
Jump to solution

You can always specify more filter properties to narrow down your search.

If it fits you, use it as it will return lesser objects to filter out afterwards and improve performance.

Don't know why there weren't any additional properties in the code though.

Arnim van Lieshout Blogging: http://www.van-lieshout.com Twitter: http://www.twitter.com/avlieshout If you find this information useful, please award points for "correct" or "helpful".
Reply
0 Kudos
cchipman
Contributor
Contributor
Jump to solution

Thanks for the suggestion, I'll try reworking it with the Get-Event.  However, I'd like to finish out the thought exercise.  I was trying to do this:

    $filter = New-Object VMware.Vim.TaskFilterSpec

    $filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime

    $filter.Time.beginTime = (($snap.Created).AddSeconds(-5))

    $filter.Time.timeType = "startedTime"

    $filter.State = "success"

    $filter.Entity = New-Object VMware.Vim.TaskFilterSpecByEntity

    $filter.Entity.recursion = "self"

    $filter.Entity.entity = Get-Vm -Name $snap.VM.Name | Get-View | Get-VIObjectByVIView

but get the following error

Get-VIObjectByVIView : 2/2/2011 1:53:20 PM    Get-VIObjectByVIView        Object reference not set to an instance of an object.   
At C:\scripts\snapreminder.ps1:59 char:89
+     $filter.Entity.entity = Get-Vm -Name $snap.VM.Name | Get-View | Get-VIObjectByVIView <<<<
    + CategoryInfo          : NotSpecified: (:) [Get-VIObjectByVIView], VimException
    + FullyQualifiedErrorId : Core_BaseCmdlet_UnknownError,VMware.VimAutomation.ViCore.Cmdlets.Commands.DotNetInterop.GetVIObjectByVIViewCommand

Any ideas on what I am doing wrong?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The property Entity needs a MoRef.

Try replacing this line

$filter.Entity.entity = Get-Vm -Name $snap.VM.Name | Get-View |  Get-VIObjectByVIView

by this

$filter.Entity.entity = (Get-Vm -Name $snap.VM.Name).Extensiondata.MoRef


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
cchipman
Contributor
Contributor
Jump to solution

That worked great.  But it looks like I've run into some time based problems....

when I get to the end of the Get-SnapshotExtra, where it's trying to see if the find the VM+Snapshot creation time in the map,

   # 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

        }

        $_

if I output the value of $key and $report it shows:

[DBG]: PS C:\Users\admin.ccc>>> $key
FSBUS&1/8/2011 5:00:44 PM
____________________________________________________________________________________________________________________________________________________________________________________________________________________
[DBG]: PS C:\Users\admin.ccc>>> $report
Name                           Value                                                                                                                                                                                  
----                           -----                                                                                                                                                                                  
FSBUS&1/8/2011 11:00:44 PM     @{User=ICXT\svc.scripts}

Now, since we are in Central time zone, the 6 hour difference *kind* of makes sense, but is there away to determine what TZ the data was in, and adjust it before we convert it to the string to put in the tables?

Thanks for all the help, you've been extremely considerate.

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

Those timestamp are kept in UTC.

You can easily convert to your localtime like this

$_.Created.ToLocalTime()


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
Zsoldier
Expert
Expert
Jump to solution

Awesome.  I had been messing w/ this for awhile trying to figure out best method.  Thanks as always.

Chris Nakagaki (中垣浩一)
Blog: https://tech.zsoldier.com
Twitter: @zsoldier
Reply
0 Kudos
cchipman
Contributor
Contributor
Jump to solution

Ok, I took all the changes, added in some of the parts from list_snapshots_v6.ps1, and have the one below.

Creates a list of snapshots, identifies who created them, and emails the owners about them, as well as emailing you a summary.

Next step is to add a custom field to VMs allowing them to be excluded from this reporting.

Reply
0 Kudos
ron9999
Contributor
Contributor
Jump to solution

Hi.

This script is very helpful for me but i have a little problem with the creator information. We have VMs who have 2 days old snapshots and the script shows under "Created by" Unknown Creator. Has someone an idea why this information isn't shown there?

Thanks for help

ron999

Reply
0 Kudos
BKeadle
Contributor
Contributor
Jump to solution

Very cool.  I've spent most of the day searching/trying scripts to do this, and yours is the best, and *almost* perfect for what I'm looking for:

- would it be easy enough to add the snapshot size to the email and summary?

- if the snapshot owner is not determined (returns "unknown owner"), do I understand correctly that you attempt to find that  information in the VC task list?

- if the owner is determined, how is the email address determined from DOMAIN\Owner?  Would it be possible to extract Owner and append the email domain name, e.g.  from DOMAIN\Owner  => Owner@mydomain.com?

Did you ever complete the "next step" you mention for exclude VMs from the report?

Reply
0 Kudos
AlbertWT
Virtuoso
Virtuoso
Jump to solution

Wow, this is so cool guys an all star thread 🙂

thanks guys.

/* Please feel free to provide any comments or input you may have. */
Reply
0 Kudos
BKeadle
Contributor
Contributor
Jump to solution

It would seem all/any snapshots the owner is unknown.   Any idea why this would be?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

The script tries to macth the Event, which holds the creator information, with a snapshot based on the timestamp availble on the snapshot and in the event.

I noticed that sometimes these timestamps might differ with a couple of seconds.

A solution would be to not look for an exact match of the timestamp, but look for an event that is "close enough". For example a couple of seconds before and after the snapshot create time.

This poses a certain risk of matching the wrong event with the snapshot, but that can be avoided by adding an additional test, for example for the VM name. It's highly unlikely that you have 2 snapshots for the same VM in a matter of seconds.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos
BKeadle
Contributor
Contributor
Jump to solution

Agreed - but *how* to do this?   Looking through the script, I see this seemingly relevant function:

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"
    $filter.State = "success"
    $filter.Entity = New-Object VMware.Vim.TaskFilterSpecByEntity
    $filter.Entity.recursion = "self"
    $filter.Entity.entity = (Get-Vm -Name $snap.VM.Name).Extensiondata.MoRef

    $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
            if ( $snapshot -ne $null)
            {
                $key = $_.EntityName + "&" + ($snapshot.CreateTime.ToLocalTime().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.ToLocalTime().ToString())
        if($report.ContainsKey($key)){
            $_ | Add-Member -MemberType NoteProperty -Name Creator -Value $report[$key].User
            write-host $report[$key].User is creator of $key
           
        }
        $_
    }
    $snapshotsExtra
}

Isn't this already adjusting by 5 seconds?  This still seems like it's testing for a specific time.  Is there a better way to do this so that I can find an entry, say +/- 10 seconds?

Reply
0 Kudos
LucD
Leadership
Leadership
Jump to solution

It reads the events starting 5 seconds before the create time it gets from the snapshot.

But later on it calculates a key, containing the time from the event, for the hashtable

$key = $_.EntityName + "&" + ($snapshot.CreateTime.ToLocalTime().ToString())

which it then later on uses the time from the snapshot to create a new key which it uses to check if there is an antry with that key in the hash table.

if($report.ContainsKey($key)){

What you could do is round the time in the key to for example a 10 seconds interval.

In other words, these time values, 21:17:10/21:17:14/21:17:19, would all become 21:17:10.

This is still not a 100% foolproof solution but should produce better matches between the create time from the snapshot and the event time.

PS: you could of course allso end up with "unknow" creators because that property is simply not there in the event.

I have seen that happen also a couple of times.


Blog: lucd.info  Twitter: @LucD22  Co-author PowerCLI Reference

Reply
0 Kudos