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: http://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 populatedwhile ($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 -eq0) {write-host "Virtual machine created."} #If the return value is not "0" or "4096" then the operation failedElseIf ($Result.ReturnValue -ne4096) {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 -eq3-or$job.JobState -eq4) {write-host $job.PercentComplete start-sleep 1#Refresh the job object$job=[WMI]$Result.job}#A jobstate of "7" means successif ($job.JobState -eq7) {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