Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
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.