Thursday, October 15, 2009

I have built my fair share of VMs using both Hyper-V Manager and Virtual Machine Manager 2008 (VMM), but as an engineer, I wanted a way to build multiple VMs without the repetitive clicking involved in doing so within the graphical tools.  Fortunately, Microsoft enabled Hyper-V to be managed through WMI scripting.  With VMM, Microsoft has extended the scripting capability into PowerShell.  For this entry, I will focus on WMI,since I always look to understand what shortcut code is doing behind the scenes.  Much of the PowerShell scripting to accomplish these tasks are shorter than their WMI counterparts, but hide the details that I want to see and understand.

For those that don’t know, WMI is the method used to manage pretty much anything Microsoft has developed that runs under the Windows Operating System, so it is fitting that we can use it to manage Hyper-V as well.  Microsoft has made available several classes in WMI that can be used to manage Hyper-V.  A full listing can be found here.

I will not get into scripting the Hyper-V host server build, but will focus on scripting the virtual machine builds on the host.  See, I am building up VMs all the time and needed a method for lighting them up quickly. Building a host or multiple host servers is nice, but not really what I would use in my scenario.

In Hyper-V, there are four primary components that  make up a virtual machine.  These are virtual disk resources, processor resources, memory resources, and network resources. I will be covering each of these parts separately with a final script that takes everything and merges them into one script that will build a virtual machine for you.

The first thing we need to do before we can play with virtual disks is to create them.  Generally, this is pretty simple with a couple of lines.

$VirtDiskSvc = Get-WmiObject -Class "Msvm_ImageManagementService" -Namespace "root\virtualization" 

$VirtDiskSvc.CreateFixedVirtualHardDisk("f:\VHDs\win2k8.vhd", 20GB)

This gets a handle to the class in the first line and then creates a disk using the CreateFixedVirtualHardDisk method using a size of 20GB and path of f:\VHDs\win2k8.vhd.
If you would like to create a dynamic disk file, just keep the first line, but replace the second line.
$VirtDiskSvc.CreateDynamicVirtualHardDisk("f:\VHDs\win2k8.vhd", 20GB)

This creates a dynamic disk using the specified path and size.  The difference between these two methods is in Fixed, the entire 20GB disk is created.  In dynamic, just the minimal size is created and the virtual disk will autogrow up to the 20GB maximum size.
Finally, Hyper-V supports differencing, which uses a base disk and writes any changes to a differencing disk.  There are a couple of additional lines that need to be run to support this.
$VirtDiskPath = "f:\VHDs\win2k8-diff.vhd"

$VirtDiskParent = "f:\VHDs\win2k8.vhd"

$VirtDiskSvc = Get-WmiObject -Class "Msvm_ImageManagementService" -Namespace "root\virtualization"

$VirtDiskSvc.CreateDifferencingVirtualHardDisk($VirtDiskPath, $VirtDiskParent)

Building on this, let’s create a complete script.

# Set up variables$VHD = "f:\VHDs\win2k8.vhd"

$GuestVM = "Win2k8"

$Namespace = "root\virtualization"

$Computer = "Hyper-V2k8"

# Get instance of Msvm_VirtualSystemManagementService class$VSMSvc = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace $Namespace -ComputerName $Computer

# Get instance of Msvm_ComputerSystem class$VM = Get-WmiObject -Namespace $Namespace -ComputerName $Computer -Query "Select * From Msvm_ComputerSystem Where ElementName='$GuestVM'"

#Associating Msvm_VirtualSystemSettingData class with $VM$VMVSSD = Get-WmiObject -Namespace $Namespace -Query "Associators of {$VM} Where ResultClass=Msvm_VirtualSystemSettingData AssocClass=Msvm_SettingsDefineState"

# Define instance of Virtual IDE controller through an association$VMIDECtrl = (Get-WmiObject -Namespace $Namespace -Query "Associators of ($VMVSSD) Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_VirtualSystemSettingDataComponent"  | where-object -FilterScript {$_.ResourceSubType -eq "Microsoft Emulated IDE Controller" -and $_.Address -eq 0})

# Define capabilities of the disk resource$DiskAllocSet = Get-WmiObject -Namespace $Namespace -Query "SELECT * From Msvm_AllocationCapabilities

WHERE ResourceSubType = 'Microsoft Synthetic Disk Drive'"

# Define minimum, maximum, default, and incremental value for disk resource allocation$DefaultDiskDrive = (Get-WmiObject -Namespace $Namespace -Query "Associators of {$DiskAllocSet} Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_SettingsDefineCapabilities" | where-object -FilterScript {$_.InstanceID -like "*&Default"})

# Define controller and address$DefaultDiskDrive.Parent = $VMIDECtrl._Path$DefaultDiskDrive.Address = 1

# Define new disk drive through AddVirtualSystemResources method of the Msvm_VirtualSystemManagementService class$NewDiskDrive = ($VSMSvc.AddVirtualSystemResource($VM._Path, $DefaultDiskDrive.PSBase.GetText(1))).NewResources

#Get Microsoft Virtual Hard Disk resource subtype$DiskAllocSet = Get-WmiObject -Namespace $Namespace -Query "SELECT * FROM Msvm_AllocationCapabilities WHERE ResourceSubType = 'Microsoft Virtual Hard Disk'"

# Get Microsoft Virtual Hard Disk instance and store in variable$DefaultHardDisk (Get-WmiObject -Namespace $Namespace -Query "Associators of {$DiskAllocSet} Where ResultClass=Msvm_ResourceAllocationSettingData AssocClass=Msvm_SettingsDefineCapabilities" | where-object -FilterScript {$_.InstanceID -like "*Default"})

# Define properties for our $DefaultHardDisk$DefaultHardDisk.Parent = $NewDiskDrive$DefaultHardDisk.Connected = $VHD

# Add $DefaultHardDisk to virtual machine$VSMSvc.AddVirtualSystemResources($VM._Path, $DefaultHardDisk.PSBase.GetText(1))

That should take care of create a virtual disk through WMI.  Next time, I will take a look at creating processor resources through WMI for Hyper-V.

0 comments: