Thursday, December 6, 2012

Setting VM IP with Hyper-V WMI

There is a hidden feature in Server 2012.  Buried deep in the bowels of the WMI classes is this little tidbit that is discoverable, but not talked about.

You can set the IP of a VM through Hyper-V 2012. 

For a long time now, in the forums we have told folks that this cannot be done.  Well, it can be.  And at small scale it works.  I have used it to set up my Network Virtualization demonstration environment.

The thing that you need to be aware of is that this is one of those cases where you send your command to WMI / CIM and you need to double back and check what happened.  Did the IP actually set? 

Be sure to check that before going off and expecting that it did.  If it didn’t, you have event logs in the VM.  Check there.

This took a bit of working through and discovery.  I never did work through IPv6, I stopped when I got IPv4 working.

<speculation>

As you might imagine, there has to be a dependency on the aligning of the version of the Integration Services within the VM and the Hyper-V Server.  I could not imagine this working with Hyper-V 2012 and a Server 2008 VM when the Integration Components in the VM have not been updated to the current Hyper-V level.

</speculation>

One thing that I did not blog about with the Network Virtualization script was how I set up my environment, more on that next, I scripted it, and it is not small.

Back the the subject, setting the IP of a VM through WMI of the Hyper-V Server.  I am going to leave out all the error handling just to make this easier to read through.

Here is a PowerShell function where you can see the WMI in action:

function Set-VMIPAddress ($VMName, $IPSettings)

{

    $Service = Get-WmiObject -Class "Msvm_VirtualSystemManagementService" -Namespace "root\virtualization\v2"

    $Query = "SELECT * FROM Msvm_ComputerSystem WHERE ElementName = '" + $VMName + "'"

    $VM = Get-WmiObject -Query $Query -Namespace "root\virtualization\v2"

    $setIP = $Service.SetGuestNetworkAdapterConfiguration($VM, $IPSettings.GetText(1))

}

Of course, you want to consider $setIP.ReturnValue

Like all WMI / CIM commands if the return value is “0” then you have success, “4096” generally means it is running.  And anything else means something went wrong in most cases.

If you have “4096” then query for the status of the job itself.  $job = [WMI]$setIP.job

By the way, if you want to get the IP of the VM don’t think $Service.GetGuestNetworkAdapterConfiguration.  This is proper thinking for PowerShell but not for WMI / CIM.  CIM could never be that easy.  ;-)

Look for the class Msvm_GuestNetworkAdapterConfiguration to get the IP of a VM.  Find the VM NIC with Msvm_SyntheticEthernetPortSettingData first.

10 comments:

Olaf said...

Excellent work, though I'm a little surprised that WMI was the way to go to get at this. I had hoped there would simply be a property through Set-VMNetworkAdapter, but of course there is not.

BrianEh said...

There may be in the future, who knows.
This is one of those little nuggets that you stumble upon, but isn't documented, and isn't in the PowerShell.
So you have to wonder. And because it is a stumble upon, it also might not be fully baked.
Like I mentioned, the error handling is not great.

I had to enable the WMI tracing in the Event Viewer under Applications and Services on the Hyper-V Server to get mine working properly - I had all kinds of formatting issues.
That blog post is next.

Olaf said...

Yeah - it's the intricacies of that IP input. It looks to me like it has to be formatted as an array a la the Msvm_GuestNetworkAdapterConfig:

class Msvm_GuestNetworkAdapterConfiguration
{
string InstanceID;
uint16 ProtocolIFType;
boolean DHCPEnabled;
string IPAddresses[];
string Subnets[];
string DefaultGateways[];
string DNSServers[];
};

BrianEh said...

Yes, you hit the required elements of the array pretty quickly.

For example, I believe that the minimum needed is (taking a snip from mine):

#Setting the VM Network settings to a static IPv4 address
$VMIP.DNSServers = @("192.168.1.2")
$VMIP.IPAddresses = @("192.168.1.11")
$VMIP.DefaultGateways = @("192.168.1.1")
$VMIP.Subnets = @("255.255.255.0")
$VMIP.DHCPEnabled = $False

Olaf said...

This is the only relevant information I found in the WMI trace.

...ExecQuery - root\virtualization\v2 : associators of {\server01\root\virtualization\v2:Msvm_SyntheticEthernetPortSettingData.InstanceID="Microsoft:ID\\ID"} where resultclass = Msvm_GuestNetworkAdapterConfiguration assocclass = Msvm_SettingDataComponent...

Did you filter for anything in particular, or just your user context in XPath?

*[UserData/Operation_New/User="domain\user"]

BrianEh said...

Quite honestly, It has been a while since I did this. Months actually.

There are a couple places in the event logs to watch WMI. I don't have the moments to dig and test. But I recall one log showing each call and response, and another giving the errors.

Trying to capture on the client was missing the actual error that I needed to determine what was actually up.

That said, it may have gotten better since I messed with it, as I seem to recall I was not on RTM at the time.

tarlov said...

This is pure gold, thanks. Any way to use this method to rename the Computer Name of a VM ?

BrianEh said...

Not at this time. Who knows what the future may bring.
However, most solutions only apply the computername on provision, and only if the image was sysprep'd - because they all use an unattend file to apply it with.

Unknown said...

Hi... I know this is an old post. But I stumbled upon it, trying to do exactly this.
I am lost at trying to get the actual Ip settings in the script.
Can you please show me the correct format or how to set the ipsettings?
$IPSettings.gettext(1) ??

Thanks in advance!!

Unknown said...

Hi,
Apologies... I found the answer. Thank you very much for sharing this incredible script!! :-)