The Hyper-v API Network interfaces
If you've read my post on adding disks to a Virtual machine, the techniques here should already feel familiar. We create a NIC , and we create a switch port. And then we tell the NIC it is connected to the switch port. Hyper-V creates VM switches which are either bound to a NIC, internal (visible to the Parent partition) or private (visible only to the child VMs). So one of the first things to do when setting up a NIC is to choose the switch, and the first function I'm going to create is Choose-VMSwitch using the choose-list function I've already shown: getting the Switches to pass to choose-List is easy enough, just query WMI for MSVM_VirtualSwitch Objects.
Function Choose-VMSwitch
{choose-list (Get-WmiObject -NameSpace "root\virtualization" -Class "MsVM_VirtualSwitch") `
@(@{Label="Switch Name"; Expression={$_.ElementName}} )
}
So now I can have a command Add-VMNic $VM (Choose-VmSwitch) . Since Hyper-V supports Legacy and VMBus NICs, I have given the option for a -Legacy switch, and to support giving the NIC a fixed MAC address I've added a -MAC switch too. The PowerShell Filter is much the same as I've shown previously
- If not passed a VM parameter pick up what is in the pipe
- If presented a string as a VM parameter replace it with a VM WMI Object (or array of VMs)
- If presented with an array of Strings or VMs call the function recursively passing it each member of the array [go back to step 2]. N.b. Multiple NICS can't have the same MAC address so ignore the -MAC parameter.
- Assuming we've got a WMI object ... get the appropriate Resource Allocation Settings Data object
- Set the properties of the RASD; including the MAC address if one was provided. If no Switch parameter was passed set the connection property to an empty string, if one was passed Call New-VmSwitchPort and set the connection property to point to that. The VMBus NIC needs a GUID as an Identifier and the Emulated one does not.
- Set up an arguments array, and call the "AddVirtualSystemResources" method of the VirtualSystemManagementService WMI object (which I get with Get-WmiObject -NameSpace "root\virtualization" -Class "MsVM_virtualSystemManagementService" ).
So here's the code in full.
Filter Add-VMNIC
{Param ($VM , $Virtualswitch, $mac, [switch]$legacy )
if ($VM -eq $null) {$VM=$_}
if ($VM -is [Array]) {if ($legacy) {$VM | ForEach-Object {add-VmNic -VM $_ -Virtualswitch $Virtualswitch -legacy} } else {$VM | ForEach-Object {add-VmNic -VM $_ -Virtualswitch $Virtualswitch} } }
if ($VM -is [String]) {$VM=(Get-VM -Machinename $VM ) }
if ($VM -is [System.Management.ManagementObject]) {
if ($Legacy) {$NicRASD = Get-VMRASD -resType 10 -resSubType 'Microsoft Emulated Ethernet Port'
$NicRASD.ElementName= "Legacy Network Adapter"}
else {$NicRASD = Get-VMRASD -resType 10 -resSubType 'Microsoft Synthetic Ethernet Port'
$NicRASD.VirtualSystemIdentifiers=@("{"+[System.GUID]::NewGUID().ToString()+"}")
$NicRASD.ElementName= "VMBus Network Adapter"}
if ($virtualSwitch -ne $null) {$Newport = new-VmSwitchport $virtualSwitch if ($Newport -eq $null) {$Newport= ""}
$NicRASD.Connection= $newPort}
if ($mac -ne $null) {$nicRasD.address = $mac $nicRasD.StaticMacAddress = $true } $arguments = @($VM.__Path, @( $nicRASD.psbase.GetText([System.Management.TextFormat]::WmiDtd20) ), $null, $null )
$result = $VSMgtSvc.psbase.invokeMethod("AddVirtualSystemResources", $arguments) if ($result -eq 0) {"Added NIC to '$($VM.elementname)'."} else {"Failed to add NIC to '$($VM.elementname)', return code: $Result" }} $vm = $null
}
In this function I call "New-VmSwitchPort", which is a wrapper for a method provided by the Virtual Switch Management Service. Like the the image management service, and the Virtual System Management Service, this is just a WMI Object which we query for. The process goes
- If presented with a string as the VirtualSwitch Parameter, replace it with a VirtualSwitch WMI object
- Assuming we now have a WMI object, get the SwitchManagementService WMI object.
- Get a GUID to use as the port's name and friendly name , and pass it, the Switch object and 2 nulls in one array to the CreateSwitchPort method
- The Path to the new port is returned in one of these nulls, so the function picks this up and returns it.
Function New-VMSwitchPort
{Param ($virtualSwitch , $Server=".")
if ($Virtualswitch -is [String]) {$Virtualswitch=(Get-WmiObject -computerName $server -NameSpace "root\virtualization" -Query "Select * From MsVM_VirtualSwitch Where elementname = '$Virtualswitch' ")}
if ($Virtualswitch -is [System.Management.ManagementObject]) { $SwitchMgtSvc=(Get-WmiObject -computerName $Virtualswitch.__server -NameSpace "root\virtualization" -Query "Select * From MsVM_VirtualSwitchManagementService") [String]$GUID=[System.GUID]::NewGUID().ToString() $arguments=@($Virtualswitch.__Path, $GUID, $GUID, $null, $null) $result = $SwitchMgtSvc.psbase.invokeMethod("CreateSwitchPort",$arguments) if ($result -eq 0) {"Created VirtualSwitchPort on '$($virtualSwitch.elementName)' " | out-host @($arguments[4]) } else {"Failed to create VirtualSwitchPort on '$($virtualSwitch.elementName)': return code: $Result" | out-host} }
}
There are some extra functions that I won't show here - I've got a "Remove-Port" function and a Set-VMNICPort function - which removes an existing port and adds a newly created one. I've got a Set-VMNICMacAddress function which changes the MAC address after the NIC is created, and Get-VMNIC and Get-VMNICSwitch which build up to give a Get-VMNICList function along the same lines as the Get-VMDiskList I showed before
Bonus link Over on the Virtualization Team blog, Taylor has posted the code to connect the Host machines Network card to a VM switch. I'm going to rework that code slightly for the library I'm building - I'll have a "Choose-ExternalEthernetPort" and so on.
Technorati Tags: Microsoft,Windows Server 2008,Hyper-v,Virtualization,PowerShell