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)

Friday, October 25, 2013

SCVMM Service deployment and NO_PARAM server: NO_PARAM: NO_PARAM

I have to say.  This particular error is my favorite of all time (so far).

Here is the scenario:

  • I deploy a Service form a Template.
  • I wait.
  • The Job fails.
  • I check the SCVMM Job log and see something resembling this:

Error (2912)

An internal error has occurred trying to contact the NO_PARAM server: NO_PARAM: NO_PARAM.

NO_PARAM

Recommended Action

Check that WS-Management service is installed and running on server NO_PARAM. For more information use the command "winrm helpmsg hresult". If NO_PARAM is a host/library/update server or a PXE server role then ensure that VMM agent is installed and running.

Error (20400)

1 parallel subtasks failed during execution.

Error (2912)

An internal error has occurred trying to contact the NO_PARAM server: NO_PARAM: NO_PARAM.

NO_PARAM

Recommended Action

Check that WS-Management service is installed and running on server NO_PARAM. For more information use the command "winrm helpmsg hresult". If NO_PARAM is a host/library/update server or a PXE server role then ensure that VMM agent is installed and running.

Error (20400)

1 parallel subtasks failed during execution.

 

I can tell you from experience that this error has absolutely nothing to do with WinRM.  In fact, if you spend time there, it is wasted.

So, what happened?

In a nutshell; your script / installer ran, and it did not throw a single error.  Not one.  But, your timeout setting was too low due to something, anything and SCVMM gave up waiting for the Exit Code 0 that your script had finished.

Recall, that there was no error, so SCVMM did not have one to pass back up the chain to you and put in the job log.  That is where all of this NO_PARAM business is coming from.  Literally, no error was passed to something as a parameter and that particular piece of code is simply stating that it didn’t receive one.

And SCVMM reports this error as an error and pattern matches it and attempts to give you some guidance around it – where the WinRM part comes from.

 

I first caused this to happen because my script was stalled with a dialog box that was open, waiting for someone to respond, and since everything you define in your Service Template runs headless, there is no way to even know the dialog appeared – other than to logon to your VM and see that the script process continues to run.

I have also seen this happen again when there is high disk IO causing the various installers or configuration scripts to actually run slower.

Just to give a few clues as to why you see this in the first place, as it is a real mystery until you figure it out.  It took me a couple weeks to sort it all out.  Now, I avoid it – I spread my VMs across my hosts by selection.

Monday, October 7, 2013

Exporting the VHD of a running VM with Hyper-V 2012

A co-worker recently asked me about how to clone / export a running VM on Hyper-V 2012.

My first reply was; “upgrade to Hyper-V 2012 R2 and it is built-in”. 

Unfortunately that didn’t meet his needs, he is stuck in the Hyper-V 2012 world for a bit.

I came up with a process, it is not a pretty process, that is within all the parameters of file locking, doing things the way that you ‘should’, etc.

The key thing to wanting to ‘clone’ or export a VM is that you really want the virtual disk.  That is the ‘state’ of the machine.  The settings are easily copied and relatively incidental, the most important part is the virtual disk. 

I say that because this entire convoluted process is all about getting a very clean virtual disk state.  In this entire process, the settings of the machine (CPU, RAM, dynamic memory, virtual switch attachment, etc.) don’t matter.  And in the real world (outside of my little perfect test world) they really don’t matter until you Import.

Enough rambling on.  So, what is this process anyway?  In a nutshell it is:

If you take a snapshot of a VM, you can then add a differencing disk to the parent disk of the snapshot, create a VM from that, export that VM, then destroy the VM, then destroy the differencing disk.

Because this is not a snapshot, with the export Hyper-V gives you the differencing disk plus the parent.
If you exported a snapshot you get a single virtual disk, since Hyper-V does special things with AVHDX files.
If you want a single file, then you merge the diff that is in the export.

I know that some of my blog readers dream in command line, so here comes the PowerShell.

Special note:  This is specific to Hyper-V 2012 and works because of live merging and the built-in PowerShell provider.  Hyper-V 2012 R2 does not need all this mess, just take a snapshot and Export.  Hyper-V 2008 or 2008 R2 does not have a built-in PowerShell provider, but you could do all this with WMI.

$vm = get-vm "datest"

# I always want 'now' so we take our own snapshot
$checkPoint = Checkpoint-VM -VM $vm -SnapshotName "clone" -Passthru

# Create a differencing disk and link it to the disk of the snapshot.
$diffVhd = New-VHD -Differencing -ParentPath $checkPoint.HardDrives[0].Path -Path ("D:\Test\" + $checkPoint.Name + ".vhdx")

# If you really care about the exact configuration of your VM and want to Import it on the other side, then do the configuration only export using WMI:  http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/03/24/performing-a-configuration-only-export-import-on-hyper-v.aspx
# on the Import side you would 'fix-up' the configuration and use the merged new disk from later on in this example.  http://itproctology.blogspot.com/2012/08/handling-import-vm-errors-in-server.html

$clone = New-VM -Name $checkPoint.Name -VHDPath $diffVhd.Path

Export-vm -VM $clone -Path D:\Test -Passthru

Remove-VM -VM $clone -Force
Remove-Item $diffVhd.Path

$vhds = Get-ChildItem -Path D:\test -Recurse -File -Include "*.vhd*" | Get-VHD

foreach ($vhd in $vhds) {
    if ($vhd.VhdType -eq "Differencing") {
        $parent = Get-Item $vhd.ParentPath

        Merge-VHD $vhd.Path -Force
    }
}

I am going to mention it again.  I am using the Export process to get a clean virtual disk, not to have a proper VM configuration.

Use Ben’s Configuration Only export to get the configuration XML.  Then on Import use the Fix-up methodology to point to the new VHD.

Sounds like I need a second blog to put this all together.

Friday, October 4, 2013

Scripted installation of the SCVMM Console

This actually seems like it would be pretty straightforward.  However, a documentation bug leaving out a critical switch leaves you guessing.

The following runs on a VM where the the SCVMM ISO is attached.  Nothing more needs to be done beyond attaching the ISO to the VM and executing the script.  This is essentially totally hands-off.

The thing you will probably want to pay attention to are the command line switches for the Console installer.

# CD-ROM selects anything in the DVD drive.  The size ensures that something is mounted.
$dvdDrives = Get-Volume | where {$_.DriveType -eq "CD-ROM" -and $_.Size -gt 0}
# Since a VM could have more than one DVD drive, and SCVMM might be using one for its own purposes we need to find the correct one.
foreach ($dvd in $dvdDrives){
    #test for the sample INI file in the right location to ensure this is the VMM media.
    Switch ([System.IntPtr]::Size)
    {
        4 {
            If (Test-Path -Path ($dvd.DriveLetter + ":\i386\") -PathType Container){
                $vmmMedia = Get-ChildItem -Path ($dvd.DriveLetter + ":\i386\Setup\") -recurse -Filter "VMClient.ini"
            }
        }
        8 {
            If (Test-Path -Path ($dvd.DriveLetter + ":\amd64\") -PathType Container){
                $vmmMedia = Get-ChildItem -Path ($dvd.DriveLetter + ":\amd64\Setup\") -recurse -Filter "VMClient.ini"
            }    
        }
    }
    If ($vmmMedia -ne $null){
        If (Test-Path $vmmMedia.FullName){
            $FilePath = (Get-ChildItem -Path $vmmMedia.PSDrive.Root -Filter "Setup.exe").FullName
        }
    }
}
if ($FilePath) {
    try {
        "Starting SCVMM Console installation."
        Get-Date -Format HH:mm:ss
        Start-Process -FilePath $FilePath -ArgumentList "/client /i /IACCEPTSCEULA" -Wait -NoNewWindow
        "Done waiting for the installer"
        Get-Date -Format HH:mm:ss
        Start-sleep 30
        "SCVMM Console installed."
        Get-Date -Format HH:mm:ss
    }
    catch {
        $Error |  Out-File $logFile -Append
    }
}
else{ Write-Error -Category ObjectNotFound -Message "The SCVMM Installation media was not detected." -RecommendedAction "Please manually install the SCVMM Console" }