Wednesday, October 30, 2013

Modifying Hyper-V Generation2 VM boot device order with PowerShell

Yes, I know, some of you are looking at this and thinking, that has to be simple.  Or, “just use the GUI”.

Well, I can tell you.  The new Generation 2 VM introduces some interesting thinking to the world of Hyper-V.

First of all, let me drop this idea:  resource references / resource definitions / resource paths – or as Hyper-V calls it “FirmwarePath”

Okay lets look at what we have.

In Hyper-V 2012 I used:

PS C:\Users\Foo> Get-VMBios gen2r2
Get-VMBios : A parameter is invalid. Generation 2 virtual machines do not support the VMBios cmdlets.  Use Get-VMFirmware and Set-VMFirmware instead.

Whoops.  Not going to set that in the VM BIOS.  And at least there is some good guidance in the error message though (I like that).

PS C:\Users\Foo> Get-VMFirmware gen2r2

VMName SecureBoot PreferredNetworkBootProtocol BootOrder
------ ---------- ---------------------------- ---------
Gen2R2 Off        IPv4                         {File, Drive, Drive, Network}

Okay, easy enough.  Before we just fed in a string and changed the order.  But, before I do that, let me jsut avoid that error altogether and dig deeper.

PS C:\Users\Foo> $gen2r2 = Get-VMFirmware gen2r2
PS C:\Users\Foo> $gen2r2.BootOrder

VMName BootType Device                                       Description          FirmwarePath
------ -------- ------                                       -----------          ------------
Gen2R2 File                                                  Windows Boot Manager \HD(2,GPT14FD3F49-A5D7-4B1E-97EF-C...
Gen2R2 Drive    Microsoft.HyperV.PowerShell.HardDiskDrive    EFI SCSI Device      \AcpiEx(VMBus,0,0)\VenHw(9B17E5A2-...
Gen2R2 Drive    Microsoft.HyperV.PowerShell.DvdDrive         EFI SCSI Device      \AcpiEx(VMBus,0,0)\VenHw(9B17E5A2-...
Gen2R2 Network  Microsoft.HyperV.PowerShell.VMNetworkAdapter EFI Network          \AcpiEx(VMBus,0,0)\VenHw(9B17E5A2-...

Wait.  Those are objects, device references.  In the CIM world they are Resource References.  Very interesting.

But, all I want is to set my VM to PXE boot. 

And, I am going to do this the long hand way just for example – because the order has the be changed by feeding the objects in.  I am assuming that bunches of you can sort that out in various ways and will gladly leave that in the comments.  :-)

Lets capture the objects:

PS C:\Users\Foo> $genFile = $gen2r2.BootOrder[0]
PS C:\Users\Foo> $genNet = $gen2r2.BootOrder[3]
PS C:\Users\Foo> $genHD = $gen2r2.BootOrder[1]
PS C:\Users\Foo> $genDVD = $gen2r2.BootOrder[2]

Now, lets set those back, in the order I want them

PS C:\Users\Foo> Set-VMFirmware -VMName Gen2R2 -BootOrder $genNet,$genFile,$genHD,$genDVD
PS C:\Users\Foo> Get-VMFirmware gen2r2

VMName SecureBoot PreferredNetworkBootProtocol BootOrder
------ ---------- ---------------------------- ---------
Gen2R2 Off        IPv4                         {Network, File, Drive, Drive}

Let me see snazzy ways that you script this to change the boot order.

(BTW - VMM 2012 R2 does not let you do this)

3 comments:

Marc said...

Thanks for the post it was very useful.

I ended up using a slightly simpler method to set the BootOrder.

Get-VMDvdDrive
$MyDVD = Get-VMDvdDrive vm003
$MyHD = Get-VMHardDiskDrive vm003
$MyNIC = Get-VMNetworkAdapter vm003

Set-VMFirmware vm003 -BootOrder $MyDVD, $MyHD, $MyNIC

Philip Elder Cluster MVP said...

We managed to get this working after some runs.

In our case our VMs have two VHDX files attached (OS/Data) so File,Net,Drive,Drive,Drive

Depending on the initial order one needs to juggle the variables:

$gen2r2 = Get-VMFirmware NameOfVM
$gen2r2.BootOrder
$genFile = $gen2r2.BootOrder[0]
$genNet = $gen2r2.BootOrder[1]
$genHD0 = $gen2r2.BootOrder[2]
$genDVD = $gen2r2.BootOrder[3]
$genHD1 = $gen2r2.BootOrder[4]
Set-VMFirmware -VMName NameOfVM -BootOrder $genHD0,$genDVD,$genHD1,$genNet,$genFile
Get-VMFirmware NameOfVM

P.

Unknown said...

Even easier:

$vmname = "nameofyourvm"
$NetAdapter = Get-VMNetworkAdapter -VMName $vmname
Set-VMFirmware -VMName $vmname -FirstBootDevice $NetAdapter

Yes, I could abbreviate it even further but this explains each step.
You could type out the name in line 1 or replace it with any number of methods to derive the name.
Then grab the network adapter, in this case assuming you run with just 1 adapter.
Lastly, set the network adapter as the first bootdevice.