Friday, November 11, 2011

Hyper-V WMI association NULL returns and stopped VMs

I spent way too much time staring at this today until it dawned on me that my VM might need to be running for certain associations to be activated and working.

All I was trying to do is to get a PowerShell script to return the MAC address of the Legacy Network Adapter of a VM.

Ben Armstrong has been more than generous at giving samples to the community and the updated associations sample (that should have given me exactly what I wanted) is here.

In his example he merrily selects his VM:

$vm = gwmi MSVM_ComputerSystem -filter "ElementName='Home Server'" -namespace "root\virtualization"

Then the very easily returns the vNIC:

$vnic = $vm.GetRelated("MSVM_EmulatedEthernetPort")

That is all fine and dandy for the example.  But I tried it on a VM that is powered off, and you know what?  I got nothing back.  A NULL came back from WMI.

I searched for quite a long time trying to figure out what I was doing wrong.

In the end, I discovered I was doing nothing wrong.  The VM has to be running for this association to return the vNIC. 

I actually find this interesting.  As once the object is created, it exists, and it has properties.  I already powered the VM on and off for the MAC address to be automatically assigned.  And then I can’t query it.  It makes little sense to me.  Maybe I can get Ben to respond and fill in that lack in my understanding.

In the end I simply modified my script.  I have to briefly power on the VM and then off again to automatically assign the MAC, so why not take advantage of that.

Here is what I simply have now:

$vm = Get-WmiObject Msvm_ComputerSystem -Filter "ElementName=$vmName" -Namespace "root\virtualization"
$vm.RequestStateChange(2) # start
$vnic = $vm.GetRelated("Msvm_EmulatedEthernetPort") # this only returns while the VM is running
$vm.RequestStateChange(3) # stop


Benjamin Armstrong said...

Why are you posing tough problems in a Friday?

Okay - as you have correctly discovered "Msvm_EmulatedEthernetPort" represents the live running instance of the ethernet port - and hence it only exists when the virtual machine is running. What it sounds like you want to get is the offline configuration information for the ethernet port. This can be found in Msvm_EmulatedEthernetPortSettingData.

Msvm_EmulatedEthernetPortSettingData represents the configuration data for the ethernet port and is always available.

Using your code above to get to the point of having a valid "$vm" you then want to do:

$vssd = $vm.getRelated("Msvm_VirtualSystemSettingData") | where {$_.SettingType -eq 3}

This gets you the configuration object for the entire virtual machine (the check on setting type ensures that you do not get snapshots - each virtual machine has one VSSD association for the actual virtual machine, plus extra VSSD associations for each snapshot, and a final bonus VSSD association for the currently active snapshot - long story short, if you do not check for settings type here your script will break as soon as there is a snapshot in the system).

Once you have the VSSD you then run:

$vmLegacyEthernet = $vssd.getRelated("Msvm_EmulatedEthernetPortSettingData")

Which will get you all instances of "Msvm_EmulatedEthernetPortSettingData". You can then get the MAC address here by looking at the "address" property.

A couple of things to note:
- Until it has been powered on once, the MAC address will be "00:00:00:00:00:00". You will still need to power on the VM if you want us to autogenerate a MAC address.
- If you want to set the MAC address yourself, you need to modify the Address property to the MAC address, set the StaticMacAddress property to true and then call Msvm_VirtualSystemManagementService and pass in the MSVM_ComputerSystem ($vm in this case) and the serialization of the modified Msvm_EmulatedEthernetPortSettingData/


BrianEh said...

I suspected as much. And I greatly thank you for the detail.
Most excellent.

BrianEh said...

A couple additional notes on this:

Getting the vssd is like the keys to the kingdom, it links everywhere for its object (and it does on other hypervisor platformas as well - this is a CIM standard)

Now, using the class Msvm_EmulatedEthernetPortSettingData the property I am looking for is "address" not "PermanentAddresses" as it is for Msvm_EmulatedEthernetPort.

Just to keep things straight.
Also, the check for the "now" state of the VM is simple, yet perfect.

I still need to power the VM on and off to have a MAC assigned as Ben stated. Now, you cannot do a RequestCahngeState of 3 and then right back to 2 - the 2 always fails as the VM is not "on" yet, it is in transition for a bit. Tha becomes a timing issue.
A simple sleep or a wait for the VM to be powered on works.