In an earlier post in this series (several posts ago now) I showed how the Msvm_virtualSystemManagementService WMI object can be used to configure resources in Hyper-V, and I started with the easy step of setting memory and CPUs which exist on a freshly created virtual machine. What about Network Cards, SCSI controllers, Disk drives and so on ? The 4 steps are the same as I outlined before i.e.
Where before I was using the ModifyVirtualSystemResources method, new resources need the AddVirtualSystemResources. Where memory and CPU had special ResourceAllocationSettingData (RASD) objects these ones are more generic and I have a "Get-vmRASD" function. This takes type and subtype arguments, here's a quick summary of them
So lets see how we use them in practice. As before I get an object for theVirtual System management service
$VSMgtSvc=Get-WmiObject -NameSpace "root\virtualization" -Class "MsVM_virtualSystemManagementService"
If I want to create a SCSI controller, the 4 steps above translate to:
$SCSIRASD=Get-VMRASD -ResType 6 -ResSubType 'Microsoft Synthetic SCSI Controller' $SCSIRASD.elementName="VMBus SCSI Controller" $arguments = @($VM.__Path, @( $SCSIRASD.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null ) $VSMgtSvc.PSbase.InvokeMethod("AddVirtualSystemResources", $arguments)
If I want to create a disk drive I change the type of Resource Allocation settings data, I either use
$diskRASD=Get-VMRASD -ResType 22 -ResSubType 'Microsoft Synthetic Disk Drive'
$diskRASD=Get-VMRASD 16 'Microsoft Synthetic DVD Drive'
The remaining 3 steps are the same, this time I have to set two properties: a parent - the disk controller, and the address on the controller (passed as a parameter named $LUN)
$diskRASD.parent=(Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE).__Path $diskRASD.address=$Lun $arguments = @($VM.__Path, @( $diskRASD.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null ) $VSMgtSvc.PSbase.InvokeMethod("AddVirtualSystemResources", $arguments)
Then to mount a disk into the drive it's either
$diskRASD=Get-VMRASD -resType 21 -resSubType 'Microsoft Virtual CD/DVD Disk' -server $vm.__Server
if I want a Hard disk or if I want a DVD disk it's
$diskRASD=Get-VMRASD -resType 21 -resSubType 'Microsoft Virtual Hard Disk' -server $vm.__Server
$diskRASD.parent=(Get-VMDrive -controller (Get-VMDiskController -vm $vm -ControllerID $ControllerID -IDE) -Lun $lun ).__Path } $diskRASD.Connection=$VHDPath $arguments = @($VM.__Path, @( $diskRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null ) $VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments)
OK: you may have noticed that I'm calling functions Get-VMRASD , Get-VMDiskController and Get-VMDrive
Get-VMDiskController returns the RASD object(s) for either SCSI or IDE disk controller(s) or for both. The only thing which is a bit awkward here is that SCSI controllers don't have an obvious ID, we just get a single RASD object if there is one controller or an array if there is more than one, so I get the one I want with | Select first | select last. I showed before how I can set-up filters to take input from the pipe, and the real version of this code does exactly that ... why will become clear in just a moment
Filter Get-VMDiskController {Param ($VM , $ControllerID, [Switch]$SCSI, [Switch]$IDE ) if ($scsi) { $controllers=Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData Where instanceId Like 'Microsoft:$($vm.name)%' and resourceSubtype = 'Microsoft Synthetic SCSI Controller' " ` -NameSpace "root\virtualization" if ($controllerID -eq $null) {$controllers} else {$controllers | select -first ($controllerID + 1) | select -last 1} } if ($IDE) { Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData Where instanceId Like 'Microsoft:$($vm.name)%\\$ControllerID%' and resourceSubtype = 'Microsoft Emulated IDE Controller' " -NameSpace "root\virtualization"} }
Then I wrote Get-VM Drive which takes a controller as a parameter (and again I can pipe one or controller(s) in to that - though I've omitted the code out for the sake of space. )
Filter Get-VMDrive {Param ($Controller, $LUN ) $CtrlPath=$Controller.__Path.replace("\","\\") Get-WmiObject -Query "Select * From MsVM_ResourceAllocationSettingData Where PARENT='$ctrlPath' and Address Like '$Lun%' " -NameSpace "root\virtualization" } }
Filter Get-VMDisk {Param ($Drive) $DrivePath=$Drive.__Path.replace("\","\\") Get-WmiObject -computerName $drive.__server -Query "Select * From MsVM_ResourceAllocationSettingData Where PARENT='$DrivePath' " -NameSpace "root\virtualization" }
So I can find the VHDs mounted used by one or more VMs with
Choose-VM -multiple | GetVmDiskController -IDE -SCSI | GetVMDrive | get-VMDisk
Or I can get more detailed information Like This
Function Get-VMDiskList {Param ($vm) foreach ($v in $vm) { foreach ($dc in (get-vmdiskcontroller -vm $v -ide -scsi)) { foreach ($drive in (get-vmdrive -controller $dc)) { get-vmdisk -drive $drive | select-object -property ` @{name="VMName"; expression={$v.elementName}}, @{name="VMGUID"; expression={$v.Name}}, @{name="ControllerName"; expression={$dc.elementName}}, @{name="ControllerInstanceID"; expression={$dc.InstanceId}}, @{name="ControllerID"; expression={$dc.instanceID.split("\")[-1]}}, @{name="DriveName"; expression={$drive.caption}} , @{name="DriveInstanceID"; expression={$drive.instanceID}}, @{name="DriveLUN"; expression={$drive.address}}, @{name="DiskPath"; expression={$_.Connection}}, @{name="DiskName"; expression={$_.ElementName}}, @{name="DiskInstanceID"; expression={$_.InstanceID}} }}} }
The Get-RASD code isn't nice. It's two WMI queries to get the name of the an object we need to create
Function Get-VMRASD {Param ($ResType, $ResSubType) $allocCapsPath= ((Get-WmiObject -NameSpace "root\virtualization" -Query "Select * From MsVM_AllocationCapabilities Where ResourceType = $ResType AND ResourceSubType = '$ResSubType'").__Path).replace('\','\\') New-Object System.Management.Managementobject((Get-WmiObject -ComputerName $server -NameSpace "root\virtualization" -Query "Select * From MsVM_SettingsDefineCapabilities Where valuerange=0 and Groupcomponent = '$AllocCapsPath'").partcomponent) }
Whilst it may not be nice, knowing the Values to pass it from the table above, the properties to set, and how to pass it into AddVirtualSystemResources allows you to create what ever disk related bits you need. In a future post I'll move on to looking at how you similar things with NICs. Then I'll have one more on creating VMs and setting the motherboard options and then I'll be posting the whole code for download - I've got some internal Microsoft people trying it out at the moment.
[Update the Original post had numerous proof reading errors - I hope there aren't any left]
If you've read my post on adding disks to a Virtual machine , the techniques here should already feel