Virtual PC Guy's Blog

-- Ben Armstrong, Hyper-V Program Manager

Talking about Virtual PC and Hyper-V at Microsoft

Developing on Windows Virtual PC / Virtual Server / Hyper-V

  • Virtual PC Guy's Blog

    Understanding WMI Job Expiration in Hyper-V

    • 0 Comments


    A long time ago – I blogged about how to handle WMI job objects in Hyper-V.  The short story here is that if you script or program against Hyper-V – chances are you will need to look at WMI jobs to find out whether requested operations have completed (operations like starting a virtual machine, creating a virtual hard disk or taking a snapshot all return their status through job objects).

    When we first started implementing WMI job objects, we built the system to update the status of a WMI job and to delete the job when the operation was completed.  There is an obvious flaw here: it was never possible to tell if an operation succeeded or failed, because the moment the operation completed (successfully or not) we would set the status on the WMI job and then immediately delete it.  Whoops.

    To deal with this we came up with a simple solution: once an operation is completed we maintain the WMI job in memory for 5 minutes before we delete it.  This gives any programs enough time to check the status and act appropriately.  However, this introduced a new problem.  Each of those WMI job objects took up memory to maintain – and on a large, active system we could waste a lot of memory keeping them around.  So a second change was made: if we find that we have over 4096 WMI job objects in memory – we start expiring the oldest jobs to reduce memory usage.

    For the most part – this system works just fine.  However – there are a couple of cases where I hear from people who accidentally hit WMI job expiration problems.  Two specific examples that come to mind are:

    • “I got a coffee, and when I came back things were all confused”

      Most of the time I hear this from developers – who decided to take a coffee break at just the wrong time in testing their code, which resulted in their code failing to find a job object that had exceeded its 5 minute time out.  There are a few places in our UI where you can hit this (specifically in our wizards) where if you sit on the right page for long enough – we will be very confused when you move to the next page.
    • “I was doing bulk operations, and started getting missing job operations”

      In this case I hear from people who have written a script to perform a bulk operation (like creating 4,000 virtual hard disks as quickly as possible).  What can happen here is that not only will their script / program have to deal with missing job objects, but if they are using the Hyper-V UI to perform other actions at the same time – it can get confused because jobs are disappearing.

    Now, for the most part the Hyper-V UI attempts to handle WMI Job expiration has elegantly as possible – and will just report that we cannot find the job associated with the requested operation and direct you to look at the event log for details.  But it is something that should be considered if you are writing your own code against our interfaces – jobs are not available eternally, and under load they can disappear quite quickly.

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    PowerShell Script for Exporting a virtual machine

    • 1 Comments


    I need to automate the process of exporting a virtual machine recently – and when I checked around I found that while there are a number of sample scripts out there that show you how to do this (some even on this blog) they are all written in VBScript.  As I am now to the stage where PowerShell is my preferred scripting language – I sat down and wrote up this short script:

    # 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 path to export to
    $ExportPath = Read-Host "Specify the path to place the exported virtual in"
     
    # 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
     
    # Export the virtual machine
    $result = $VMMS.ExportVirtualSystem($VM, $true, $ExportPath)
     
    # Check to make sure we succeeded
    $exportSucceeded = ProcessResult $result "Virtual machine exported." "Failed to export virtual machine."

    Some quick notes to make about this script:

    • This script takes a Hyper-V server name, a virtual machine name and an export path – and then performs a full export to the requested location.
    • I am using the older (deprecated) ExportVirtualSystem method here – and not ExportVirtualSystemEx (which I really should do).  The main reason for this is that while ExportVirtualSystemEx is a lot more powerful than ExportVirtualSystem, ExportVirtualSystem works perfectly fine for this basic use case and is much easier to script.

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Using CPU Reserve to enforce virtual processor ratio

    • 1 Comments


    On Monday I posted a script for configuring virtual machine CPU scheduler settings.  This script got me to thinking about another use for the virtual machine CPU reserve.

    You see, it can also be used to ensure that you do not unintentionally start too many virtual machines at once.

    If you were to set the CPU reserve on each virtual machine at 20% (or at 20,000 using the underlying API) then it is not possible to start extra virtual machines once you hit a ratio of 5 virtual processors for each physical processor.  This is actually what System Center Virtual Machine Manager does to enforce limits on the system.

    Here is a script that will do just that:

    # 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}
           Else
              {write-host $failureString
              write-host "ErrorCode:" $job.ErrorCode
              write-host "ErrorDescription" $job.ErrorDescription}
           }
    }
     
    # 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 new CPU reservation
    $NewReservation = Read-Host "Specify the CPU reservation (from 0-100000)"
     
    # Get the management service
    $VMMS = gwmi -namespace root\virtualization Msvm_VirtualSystemManagementService -computername $HyperVServer
     
    # Get all VSSDs for non-snapshots
    $VSSDs = gwmi "MSVM_VirtualSystemSettingData" -namespace "root\virtualization" -computername $HyperVServer | ? {$_.SettingType -eq 3}  
     
    foreach ($VSSD in $VSSDs)
       {
       # Get the related VM
       $VM = $VSSD.getRelated("MSVM_ComputerSystem") | select -first 1
       
       # Get the processor setting data
       $ProcSetting = $VSSD.getRelated("Msvm_ProcessorSettingData") | select -first 1
     
       # Update ProcSetting with the new value
       $ProcSetting.Reservation = $NewReservation
     
       # Apply the changes to the processor setting data back to the virtual machine
       $result = $VMMS.ModifyVirtualSystemResources($VM, $ProcSetting.GetText(1))
      
       # Process the result
       $successMessage = "Updated processor scheduling settings on '" + $VM.ElementName + "'"
       $failureMessage = "Failed to update processor scheduling settings on " + $VM.ElementName + "'"
       ProcessResult $result $successMessage $failureMessage
       }

    If you run this script and specify “20000” then you will be able to run at a ratio of 5 virtual processors for each physical processor.  If you run this script and specify “25000” then you will be able to run at a ratio of 4 virtual processors for each physical processor.

    Note that this will not apply to any virtual machine snapshots, or to any newly created virtual machines, so it is a little fragile as a solution.

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Setting VM CPU scheduler settings with PowerShell [Hyper-V]

    • 0 Comments


    A while ago I did a number of posts talking about the different controls that are available to you in regards to how Hyper-V schedules virtual machine processor resources (Hyper-V CPU Scheduling–Part 1, Hyper-V CPU Scheduling–Part 2, Hyper-V CPU Scheduling–Part 3 and Hyper-V CPU Scheduling–Part 4).  Today I want to share a PowerShell script that shows you how to configure these settings programmatically:

    # 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}
           Else
              {write-host $failureString
              write-host "ErrorCode:" $job.ErrorCode
              write-host "ErrorDescription" $job.ErrorDescription}
           }
    }
     
    # 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"
     
    # 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
     
    # SettingType = 3 ensures that we do not get snapshots
    $SystemSettingData = $VM.getRelated("Msvm_VirtualSystemSettingData") | where {$_.SettingType -eq 3}
     
    # Get the processor setting data
    $ProcSetting = $SystemSettingData.getRelated("Msvm_ProcessorSettingData") | select -first 1
     
    # Get new values from the user
    $NewReservation = Read-Host "Specify the CPU reserve (from 0-10000) [Currently:"$ProcSetting.Reservation"]"
    $NewLimit = Read-Host "Specify the CPU limit (from 0-10000) [Currently:"$ProcSetting.Limit"]"
    $NewWeight = Read-Host "Specify the CPU weight (from 0-10000) [Currently:"$ProcSetting.weight"]"
     
    # Update ProcSetting with the new values
    $ProcSetting.Reservation = $NewReservation
    $ProcSetting.Limit = $NewLimit
    $ProcSetting.weight = $NewWeight
     
    # Apply the changes to the processor setting data back to the virtual machine
    $result = $VMMS.ModifyVirtualSystemResources($VM, $ProcSetting.GetText(1))
     
    # Process the result
    ProcessResult $result "Updated processor scheduling settings." "Failed to update processor scheduling settings."

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    How many virtual processors does a virtual machine have?

    • 1 Comments


    A recent forum poster asked:

    How do I write a script that figures out how many virtual processors a virtual machine has, when it is offline (i.e. not running)?

    His question showed that he had made a classic mistake when working with the Hyper-V WMI APIs.  You see Hyper-V usually has two WMI objects for many parts of the virtual machine – one of them representing the settings and one of them representing the live object itself.

    Case in hand: for a virtual processor you have “MSVM_Processor” which represents the live object, and you have “MSVM_ProcessorSettingData” which represents its settings.

    Looking at the wrong object at the wrong time can cause problems.  Live objects should be used for gathering usage information about running virtual machines – settings object should be used for getting and setting configuration details.  90% of the time – the settings object is what you want (and 90% of the time it is the object that ends with the string “SettingData”).

    Here is a simple script that gets a virtual machines processor count using both objects:

    # 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"
     
    # 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
     
    # Set the stage
    Write-host "There are two ways to get a virtual machines processor count, the wrong way and the right way..."
    write-host
     
    # Let's do the wrong way first
     
    # Get the virtual processor objects
    $vProcs = $vm.GetRelated("MSVM_Processor")
     
    write-host
    Write-host "Doing it the wrong way (using Msvm_Processor):"
    write-host
    write-host "The" $VMName "virtual machine is configured with" $vProcs.count "virtual processors."
     
    # Now let's do it the right way
     
    # SettingType = 3 ensures that we do not get snapshots
    $SystemSettingData = $VM.getRelated("Msvm_VirtualSystemSettingData") | where {$_.SettingType -eq 3}
     
    # Get the processor setting data
    $ProcSetting = $SystemSettingData.getRelated("Msvm_ProcessorSettingData") | select -first 1
     
    write-host
    Write-host "Doing it the right way (using Msvm_ProcessorSettingData):"
    write-host
    write-host "The" $VMName "virtual machine is configured with" $ProcSetting.VirtualQuantity "virtual processors."

    If I run this on a running virtual machine – both methods return the same thing:

    image

    But if the virtual machine is not running – only the MVSM_ProcessorSettingData actually tells me what I want to know:

    image

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Deleting a snapshot sub-tree–via PowerShell [Hyper-V]

    • 0 Comments


    Here is a dangerous script!  Given a virtual machine name and a snapshot name, it will delete the specified snapshot *and* any snapshots underneath it in the snapshot tree that Hyper-V manager displays:

    # 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 snapshot to delete the tree from
    $SnapshotName = Read-Host "Specify the name of the snapshot to delete the tree from (warning - a lot of snapshots will be deleted)"
     
    # 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
     
    # Find the snapshot that we want to delete
    $Snapshot = gwmi -Namespace root\virtualization -Query "Associators Of {$VM} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" | where {$_.ElementName -eq $SnapshotName} | select -first 1
     
    # Delete the snapshot
    $result = $VMMS.RemoveVirtualSystemSnapshotTree($Snapshot)
     
    # Check to make sure we succeeded
    $deleteSucceeded = ProcessResult $result "Deleted snapshot tree." "Failed to delete snapshot tree."

    Note that this script actually uses a different WMI method to delete the snapshot sub-tree (when compared to deleting a single snapshot).  There is no way to stop the deletion once it is started, and there is no confirmation prompt in this script – so be careful when you use it!

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Deleting a snapshot–via PowerShell [Hyper-V]

    • 0 Comments


    Here is a script that will allow you to delete a single snapshot from a Hyper-V virtual machine:

    # 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 snapshot to delete
    $SnapshotName = Read-Host "Specify the name of the snapshot to delete"
     
    # 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
     
    # Find the snapshot that we want to delete
    $Snapshot = gwmi -Namespace root\virtualization -Query "Associators Of {$VM} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" | where {$_.ElementName -eq $SnapshotName} | select -first 1
     
    # Delete the snapshot
    $result = $VMMS.RemoveVirtualSystemSnapshot($Snapshot)
     
    # Check to make sure we succeeded
    $deleteSucceeded = ProcessResult $result "Deleted snapshot." "Failed to delete snapshot."

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Applying a snapshot–via PowerShell [Hyper-V]

    • 2 Comments


    Now that we know how to take a snapshot, and how to list snapshots – let’s start applying snapshots:

    # 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 snapshot to apply
    $SnapshotName = Read-Host "Specify the name of the snapshot to apply"
     
    # 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
     
    # Find the snapshot that we want to apply
    $Snapshot = gwmi -Namespace root\virtualization -Query "Associators Of {$VM} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" | where {$_.ElementName -eq $SnapshotName} | select -first 1
     
    # Apply the snapshot
    $result = $VMMS.ApplyVirtualSystemSnapshot($VM, $Snapshot)
     
    # Check to make sure we succeeded
    $applySucceeded = ProcessResult $result "Snapshot applied." "Failed to apply snapshot."

    This is a fairly standard Hyper-V WMI script.  One thing to be aware of is that this operation will fail if the virtual machine is running when you try to apply the snapshot – the virtual machine needs to be turned off or put into a saved state first.

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Listing snapshots–via PowerShell [Hyper-V]

    • 1 Comments


    After the relatively trick “taking a snapshot” script – today’s snapshot script is quite easy.  Listing the snapshots that a virtual machine has:

    # 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"
     
    # 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
     
    # Get snapshot objects associated with the virtual machine
    $Snapshots = gwmi -Namespace root\virtualization -Query "Associators Of {$VM} Where AssocClass=Msvm_ElementSettingData ResultClass=Msvm_VirtualSystemSettingData" 
     
    write-host
    write-host $VMName "has the following snapshots:" 
     
    # Display a formatted list of snapshots
    $Snapshots | sort CreationTime | Format-Table -Property @{n='Snapshot Name';e={$_.ElementName}}

    One thing to point out here is that I am using a WMI query that looks at the association class (MSVM_ElementSettingData) as well as the result class (MSVM_VirtualSystemSettingData).  The reason why I do this is that if you grab all MSVM_VirtualSystemSettingData classes that are associated with a virtual machine – you will get two extra entries: one for the active virtual machine, and one for the latest snapshot (which results in one of your snapshots being duplicated).  The method that I am using here ensures that you only get snapshots, and you do not get any duplicates.

    Cheers,
    Ben

  • Virtual PC Guy's Blog

    Taking a snapshot–via PowerShell [Hyper-V]

    • 0 Comments


    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

Page 1 of 14 (137 items) 12345»