I have seen a lot of people asking how they can get the username for the user who originally created a snapshot,  This is not data that is available in an obvious way from vSphere either through the GUI or PowerCli.  So I have decided to put together a quick guide and code block that could be re-used fairly easily.

The basic overview of what needs to be done, is to get a list of snapshots to work with using the Get-Snapshot CMDlet, we can then work our way through the snapshots and for-each one query the task API for the snapshot creation event at the same time the snapshot was created.  This task will have the requesting username as a property.

First off we need to load the VMware PowerShell snap-in, if you are running this in the PowerCli console this will already be done, we can also connect to vCentre.

Add-PSSnapin vmware.vimautomation.core

Connect-VIServer -Server "vcenter.domain.com" -User "domain\user" -Password "Password"

Once we have the snap-in and are connected to vCenter, we can now load a list of the snapshots to work with in to a variable

$Snapshot = Get-Snapshot "SnapshotName"

For now we are only getting one snapshot to get the username for to keep it simple. The next part is to use Get-View to load the task manager API so we can start to get the data we need from the TaskCollector.

$TaskMgr = Get-View TaskManager

Now we can create a filter to use on the task collector to narrow down the number of tasks we need to go through.

Creating the filter:

$Filter = New-Object VMware.Vim.TaskFilterSpec

Setting to only retrieve objects +/- 5 seconds from the time the snapshot was created

$Filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime
$Filter.Time.beginTime = ((($Snapshot.Created).AddSeconds(-5)).ToUniversalTime())
$Filter.Time.timeType = "startedTime"
$Filter.Time.EndTime = ((($Snapshot.Created).AddSeconds(5)).ToUniversalTime())

We also want to make sure the task completed successfully as we know the snapshot was created

$Filter.State = "success"

Finally we are checking that the task was carried out on the VM the snapshot was created on, the MoRef is required for this

$Filter.Entity = New-Object VMware.Vim.TaskFilterSpecByEntity
$Filter.Entity.recursion = "self"
$Filter.Entity.entity = (Get-Vm -Name $Snapshot.VM.Name).Extensiondata.MoRef

Now we have the filter set up we can finally go and use the task collector API to get the task that match the filter we have set up. First we use Get-View again to create the Task collector and attach our filter.

$TaskCollector = Get-View ($TaskMgr.CreateCollectorForTasks($Filter))

When the task collector actually presents data in a scroll-able view and where the data has been collected it will be at the bottom of that view, so we now need a command to rewind the view back to the top of the scroll, the Out-Null is just  preventing output to the console from the command.

$TaskCollector.RewindCollector | Out-Null

Now we can read all the events from the task collector in to a variable, I am limiting it to only reading 100 entries which should be enough but can be increased or decreased as necessary.

$Tasks = $TaskCollector.ReadNextTasks(100)

We now have an array of tasks, hopefully including the one relevant to what we need.  We now need to step through the tasks we have collected and find our exact match and when we have the match create a variable with the username from the task object.

FOREACH ($Task in $Tasks)
    {
    $GuestName = $Snapshot.VM
    $Task = $Task | where {$_.DescriptionId -eq "VirtualMachine.createSnapshot" -and $_.State -eq "success" -and $_.EntityName -eq $GuestName}
    IF ($Task -ne $null)
        {
        $SnapUser = $Task.Reason.UserName
        }
    }

We now have the username stored in a variable we now need to destroy the task collector, this is because there is a limit of 32 active collectors so if this is being done on large numbers of snapshots the limit could easily be reached.

$TaskCollector.DestroyCollector()

Finally we can add a property to our $snapshot variable for the CreatedBy username and output the variable from the script.

$Snapshot | Add-Member MemberType NoteProperty Name CreatedBy Value $SnapUser
Write-Output $Snapshots

That is it for this unfortunately complicated way of getting the information, hopefully in the future this will be built in to the get-snapshot command and be a lot easier to do.  However this process does seem to be fairly reliable, it does sometimes have issues if the time on the ESX hosts is not synchronized with the time on the vCenter server.

Now just to close off I will include the whole script which is wrapped in a foreach loop so that it can process more than one snapshot, but works exactly the same way as above.

$Snapshots = Get-VM | Get-Snapshot

FOREACH ($Snapshot in $Snapshots)
                        {
                        $TaskMgr = Get-View TaskManager

                        $Filter = New-Object VMware.Vim.TaskFilterSpec
                        $Filter.Time = New-Object VMware.Vim.TaskFilterSpecByTime
                        $Filter.Time.beginTime = ((($Snapshot.Created).AddSeconds(-5)).ToUniversalTime())
                        $Filter.Time.timeType = "startedTime"
                        $Filter.Time.EndTime = ((($Snapshot.Created).AddSeconds(5)).ToUniversalTime())
                        $Filter.State = "success"
                        $Filter.Entity = New-Object VMware.Vim.TaskFilterSpecByEntity
                        $Filter.Entity.recursion = "self"
                        $Filter.Entity.entity = (Get-Vm -Name $Snapshot.VM.Name).Extensiondata.MoRef

                        $TaskCollector = Get-View ($TaskMgr.CreateCollectorForTasks($Filter))
                        $TaskCollector.RewindCollector | Out-Null
                        $Tasks = $TaskCollector.ReadNextTasks(100)

                        FOREACH ($Task in $Tasks)
                            {
                            $GuestName = $Snapshot.VM
                            $Task = $Task | where {$_.DescriptionId -eq "VirtualMachine.createSnapshot" -and $_.State -eq "success" -and $_.EntityName -eq $GuestName}
                            IF ($Task -ne $null)
                                {
                                $SnapUser = $Task.Reason.UserName
                                }
                            }

                        $TaskCollector.DestroyCollector()

                        $Snapshot | Add-Member MemberType NoteProperty Name CreatedBy Value $SnapUser
                        Write-Output $Snapshots
                        }

Comments