Taking a snapshot–via PowerShell [Hyper-V]

This week I am going to be sharing a bunch of scripts that show you how to interact with virtual machine snapshots in Hyper-V.  And I am going to start off with the hardest one – taking a snapshot:

 # Function for handling WMI jobs / return values
 Function ProcessResult($result, $successString, $failureString)
 {
    #Return success if the return value is "0"
    if ($result.ReturnValue -eq 0)
       {write-host $successString} 
  
    #If the return value is not "0" or "4096" then the operation failed
    ElseIf ($result.ReturnValue -ne 4096)
       {write-host $failureString " Error value:" $result.ReturnValue}
  
    Else
       {#Get the job object
       $job=[WMI]$result.job
  
       #Provide updates if the jobstate is "3" (starting) or "4" (running)
       while ($job.JobState -eq 3 -or $job.JobState -eq 4)
          {write-host $job.PercentComplete "% complete"
           start-sleep 1
  
           #Refresh the job object
           $job=[WMI]$result.job}
  
        #A jobstate of "7" means success
        if ($job.JobState -eq 7)
           {write-host $successString
           return $true}
        Else
           {write-host $failureString
           write-host "ErrorCode:" $job.ErrorCode
           write-host "ErrorDescription" $job.ErrorDescription
           return $false}
        }
 }
  
 # Prompt for the Hyper-V Server to use
 $HyperVServer = Read-Host "Specify the Hyper-V Server to use (enter '.' for the local computer)"
  
 # Prompt for the virtual machine to use
 $VMName = Read-Host "Specify the name of the virtual machine"
  
 # Prompt for the name of the new snapshot
 $SnapshotName = Read-Host "Specify the name of the new snapshot"
  
 # Get the management service
 $VMMS = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService -computername $HyperVServer
  
 # Get the virtual machine object
 $VM = gwmi MSVM_ComputerSystem -filter "ElementName='$VMName'" -namespace "root\virtualization" -computername $HyperVServer
  
 # Take the snapshot
 $result = $VMMS.CreateVirtualSystemSnapshot($VM)
  
 # Process the results
 $snapshotSucceeded = ProcessResult $result "The snapshot was taken." "Failed to take snapshot."
  
 # If the snapshot is created - rename it
 if ($snapshotSucceeded)
     {
     # Get the job object from the results
     $job2=[WMI]$result.job
     
     # Get the snapshot that is assocated with the completed job
     $newSnapshot = $job2.GetRelated("Msvm_VirtualSystemSettingData") | select -first 1
     
     # Change the "ElementName" 
     $newSnapshot.ElementName = $SnapshotName
     
     # Apply the changes to the snapshot
     $result2 = $VMMS.ModifyVirtualSystem($VM, $newSnapshot.GetText(1))
     
     # Check to see if renaming the snapshot succeeds
     $renameSucceeded = ProcessResult $result2 "The new snapshot was renamed." "Failed to rename the new snapshot."
     }

The tricky part of this script is not actually taking the snapshot (which is done with CreateVirtualSystemSnapshot) but naming the snapshot after it is taken (Hyper-V always auto-generates a snapshot name for you – and you need to rename it if you want to have a custom snapshot name).  Actually, that isn’t hard either – the hard bit is *finding* the snapshot after it is taken in order to name it.

The secret here is that the new snapshot object (in WMI) will actually be associated with the job object once the snapshot is complete.  So this script waits for the snapshot job to complete, then it gets the related system setting data from the completed job, and uses that to rename the snapshot.  Once you have figured this bit out – the rest is quite straight forward.

Cheers,
Ben

TakeSnapshot.zip