Hyper-V WMI: Creating/Applying/Deleting Virtual Machine Snapshots

After I finished writting this I relized it got a bit long... - I guess I had a lot to talk about!  Hope you enjoy it (or at least find it mildly useful)

imageHyper-V implements a feature known as snapshots, this should not be confused with a SAN snapshot or a VSS snapshot these snapshots are quite different.  A Hyper-V snapshot allows you to save off the current state of a virtual machine even while the virtual machine is running or paused.  You can then you can revert back to this state at a latter point…  You can create tree’s of snapshots (see screen shot), think about application testing when you might need an XP RTM image, XP SP1, XP SP2, XP SP1 that was upgraded to SP2 and then rolled back to SP1 etc…  With a snapshot tree you can create all of these interesting combinations without having to reinstall a physical or virtual machine 20 times… and when the next service pack comes out you can just apply the interesting base snapshots create a new snapshot and install the service pack – Major Time Saver!

The way a snapshot works is that Hyper-V creates a copy of the virtual machine configuration (network connections, disk configuration, cpu count, ram count etc…) Hyper-V then creates a special diffrencing VHD file known as an AVHD for every virtual hard disk connected to the VM to prevent the current VHD from being changed.  If the virtual machine is running a full copy of the guest memory is also saved – unlike a saved state the virtual machine continues to run while this is occurring.  There are a few things that should be noted, since Hyper-V effectively uses a diffrencing VHD to branch the hard disk state VM’s that are using passthough disks or cd’s can not be snapshoted.  Along the same lines just like diffrencing VHD files have a disk read and write performance penalty over a fixed or even dynamically expanding VHD snapshots to incur this cost.  Another thing to note is that the AVHD file and saved memory file by default is located in the same location as the virtual machine configuration, this can be changed either by creating the virtual machine in a non-default location or more comely in the Virtual Machine Settings’s UI under Management (Snapshot File Location).

Snapshot’s are excellent for test and development they are a huge time saver.  They are also great for staging up a server you get the ability to undo when you accidentally type the wrong machine name or password… Just remember to merge your snapshots before putting the server into production, to merge a snapshot you just deleted the snapshot tree with the virtual machine off, it will then start a background merge which you can monitor from the Hyper-V management UI.  When the merge is completed you will have a flat VHD that you can then put into production with no-performance impact.  Now let’s see some code!

Taking a Snapshot
$VMManagementService = Get-WmiObject -Namespace root\virtualization -Class Msvm_VirtualSystemManagementService $SourceVm = Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='VmToSnapshot'" $result = $VMManagementService.CreateVirtualSystemSnapshot($SourceVm) #ProcessWMIJob($result)
Applying a Snapshot
$VMManagementService = Get-WmiObject -Namespace root\virtualization -Class Msvm_VirtualSystemManagementService $SourceVm = Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName= 'VmToSnapshot' " $Snapshot = Get-WmiObject -Namespace root\virtualization -Query "Associators Of {$SourceVm} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" $result = $VMManagementService.ApplyVirtualSystemSnapshot($SourceVm, $Snapshot) #ProcessWMIJob($result)
Deleting a Snapshot
$VMManagementService = Get-WmiObject -Namespace root\virtualization -Class Msvm_VirtualSystemManagementService $SourceVm = Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='VmToSnapshot'" $Snapshot = Get-WmiObject -Namespace root\virtualization -Query "Associators Of {$SourceVm} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" $result = $VMManagementService.RemoveVirtualSystemSnapshot($Snapshot) #ProcessWMIJob($result)
Enumerating Snapshots
$SourceVm = Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='VmToSnapshot'" Get-WmiObject -Namespace root\virtualization -Query "Associators Of {$SourceVm} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" | Format-List -Property ElementName, InstanceID
Enumerating Snapshots – With a bit more information…
$SourceVm = Get-WmiObject -Namespace root\virtualization -Query "Select * From Msvm_ComputerSystem Where ElementName='VmToSnapshot'" $Snapshots = Get-WmiObject -Namespace root\virtualization -Query "Associators Of {$SourceVm} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" $script:list = @() foreach ($Snapshot in $Snapshots) {     $SnapObj = New-Object -TypeName System.Object     $SnapObj | Add-Member -MemberType NoteProperty -Name SnapshotName -Value $Snapshot.ElementName     $SnapObj | Add-Member -MemberType NoteProperty -Name SnapshotID -Value $Snapshot.InstanceID     $SnapObj | Add-Member -MemberType NoteProperty -Name SnapshotParentName -Value ([WMI]$Snapshot.Parent).ElementName     $script:list += $SnapObj } $script:list
ProcessWMIJob Function

Put this block at the top of your script and uncomment the ProcessWMIJob call if you want the function call to wait for completion and provide error messages…

function ProcessWMIJob {     param ( [System.Management.ManagementBaseObject]$Result )     if ($Result.ReturnValue -eq 4096) {         $Job = [WMI]$Result.Job         while ($Job.JobState -eq 4) {             Write-Progress $Job.Caption "% Complete" -PercentComplete $Job.PercentComplete             Start-Sleep -seconds 1             $Job.PSBase.Get() }         if ($Job.JobState -ne 7) {             Write-Error $Job.ErrorDescription             Throw $Job.ErrorDescription }         Write-Progress $Job.Caption "Completed" -Completed $TRUE }     elseif ($Result.ReturnValue -ne 0) {         Write-Error "Hyper-V WMI Job Failed!"         Throw $Result.ReturnValue } } 

ENJOY!

Taylor Brown
Hyper-V Integration Test Lead
https://blogs.msdn.com/taylorb

clip_image001