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
Comments
Anonymous
June 26, 2013
Ben, It would be great if you can provide WMI query vhds and snapshot files. Msvm_ResourceAllocationSettingData no longer gives the absolute path to vhd in rootvirtualizationv2 namespace.Anonymous
August 13, 2015
Do you have any of these examples in C#? I'm having a really hard time attaching passthrough disks utilizing WMI in C#. I'm slowly getting there converting basically all of the scripts you've written since 2008 to c# for a project I'm doing. It's quite painful, luckily the bright side is that I'm learning a lot. It's just at a snails pace.Anonymous
September 13, 2016
Is it possible to create a VM using provided VMID (GUID) and not one generated randomly by system ? The Import-VM has an option for "in-place" importing which keeps the VMID from the import xml file, which allows to keep the vmid as at the time of the export. Therefore I presume there is a possibility to do this using WMI calls.