Creating a Virtual Machine with WMI v2

Most people use the built in Hyper-V PowerShell cmdlets to control Hyper-V these days.  That said, there are people out there who program directly to our WMI APIs.  In Windows Server 2012 we introduced a new WMI namespace – and recently I have been getting a number of questions about how to convert code that talks to our old WMI namespace to the new one.

With that in mind – I am going through blog posts that I have done on programming our WMI APIs and publishing updated versions that use the new namespace.

Today I am going to look at creating a virtual machine.  The sample script for doing this with the WMI v1 namespace is here: https://blogs.msdn.com/b/virtual_pc_guy/archive/2008/05/28/scripting-vm-creation-with-hyper-v.aspx. And here is how you do the same thing in the WMI v2 namespace:

 # Prompt for the Hyper-V Server to use
$HyperVServer = Read-Host "Specify the Hyper-V Server to create the virtual machine on"
 
# Get name for new VM
$VMName = Read-Host "Specify the name for the new virtual machine"
 
# Create new MSVM_VirtualSystemSettingData object
$wmiClassString = "\\" + $HyperVServer + "\root\virtualization\v2:Msvm_VirtualSystemSettingData"
$wmiClass = [WMIClass]$wmiClassString
$newVSSD = $wmiClass.CreateInstance()
 
# wait for the new object to be populated
while ($newVSSD.Properties -eq $null) {}
 
# Set the VM name
$newVSSD.Properties.Item("ElementName").value = $VMName
 
# Get the VirtualSystemManagementService object
$VMMS  = gwmi MSVM_VirtualSystemManagementService -namespace "root\virtualization\v2" -computername $HyperVServer
 
# Create the VM
$result = $VMMS.DefineSystem($newVSSD.GetText(1))
 
#Return success if the return value is "0"
if ($Result.ReturnValue -eq 0)
   {write-host "Virtual machine created."} 
 
#If the return value is not "0" or "4096" then the operation failed
ElseIf ($Result.ReturnValue -ne 4096)
   {write-host "Failed to create virtual machine"}
 
  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
       start-sleep 1
 
       #Refresh the job object
       $job=[WMI]$Result.job}
 
     #A jobstate of "7" means success
    if ($job.JobState -eq 7)
       {write-host "Virtual machine created."}
      Else
       {write-host "Failed to create virtual machine"
        write-host "ErrorCode:" $job.ErrorCode
        write-host "ErrorDescription" $job.ErrorDescription}
   }

These two scripts look very similar.  But there are some key differences:

  • The script uses the "root\virtualization\v2” namespace instead of the “root\virtualization”
    • It surprises me the number of people that email me with questions about WMI code who do not know which WMI namespace they are using.  A simple search of your code for “root\virtualization” will give you the answer quickly.
  • The script uses the DefineSystem method instead of DefineVirtualSystem method to actually create the virtual machine
    • This is a simple name change to better align with the latest DMTF standard.  The functionality of this API is the same.
  • A number of “psbase” objects have been removed from the script
    • This is actually nothing to do with the new WMI namespace, but is instead because I am writing this script in a more recent version of PowerShell.  PowerShell v2 required that you use .psbase to access the properties of a WMI object.  PowerShell v3 does not.
  • The script creates a new MSVM_VirtualSystemSettingData instead of creating a new MSVM_VirtualSystemGlobalSettingData

The reason for this last change is quite interesting (to me – anyway) as it is the result of a philosophical change in the underlying platform. 

When we started designing virtual machine management and virtual machine snapshot management in Hyper-V we had a simple premise: virtual machine snapshots should just be read-only virtual machine instances.  This meant that at a WMI level we wanted to use the same object (VirtualSystemSettingData) to describe a virtual machine or a virtual machine snapshot.  Applying a virtual machine snapshot would then just update the “active” VirtualSystemSettingData. 

However, we encountered an issue with this approach. 

When you apply a virtual machine snapshot you do not actually want to copy the whole VirtualSystemSettingData over to the active virtual machine – for example: you do not want to change the virtual machine name when you apply a snapshot, but you do want the snapshot to have a different name.  We solved this problem by creating the VirtualSystemGlobalSettingData object.  This was a class that would store any virtual machine settings that we did not want to change when you applied a snapshot.

The problem with this approach is that it required someone who was coding against the Hyper-V WMI API to be aware of which properties were global and which were local. 

With the WMI v2 namespace we did something more intelligent – we put all the settings on the VirtualSystemSettingData and when you apply a snapshot we just do not apply the fields that we do not want to overwrite.  This means that for developers:

  • All settings belonging to a virtual machine are now in one place, not two.
  • When you create a new virtual machine you now create a VirtualSystemSettingData object, not a VirtualSystemGlobalSettingData object

Cheers,

Ben