I ran into an interesting situation last week while working with Tony Sanchez from our Global Microsoft Team. He was setting up XenDesktop 4 on Hyper-V 2008 R2. However, the lab he was at used a Windows Deployment Server (WDS) for image management and it relies extensively on PXE. Rather than modify the WDS to support the Provisioning Services PXE boot file, we decided the best solution was to make a boot ISO that will load the OS directly from the Provisioning Services host and then boot the guests from that ISO.

Background

Since not all my readers are familiar with using the Boot Device Manager, I will set the stage. When configuring a virtual machine to boot off of a CD-ROM image for PVS, you need to do three things:

  1. Add a Legacy network card on the host since the Synthetic network adapter is not created until the Operating System loads.
  2. Configure the BIOS boot order so that CD-ROM is first in the list.
  3. Assign a bootable ISO image to the CD-ROM/DVD drive.
Take Note
The PXE boot option is required in order for the NIC Option ROM to stay resident in memory during the pre-boot process. This way, UNDI will be available to the boot device to properly initialize the NIC. Otherwise, the “API not found” message would be displayed by the boot device.

In deployments of XenDesktop where you have more than about 15 machines, the XenDesktop Setup Wizard (XDSW) is normally used to create and link the XenDesktops with Hyper-V. Unfortunately, the XDSW does not support all the possible VM configuration options when duplicating the source virtual machine. One of the properties that is not transferred to the new virtual machine is the ISO in the DVD drive. Normally, this behavior is the preferred because Hyper-V needs a special configuration to support sharing an ISO across multiple guests simultaneously (See this Technet article), which if not configured correctly can cause startup issues.

If you do not want to configure ISO sharing, you can use the VMM server and VMM library to copy the boot ISO to each virtual machine’s folder. If the ISO was large, I would say spend time setting up the sharing configuration; however, in this case the file itself is only 300K and copying it will eliminate the possibility of file sharing/locking issues.

Now you understand some of the challenges, I can tell you the three steps to a PXE-free Hyper-V deployment.:

  1. Create a PVS Boot ISO using the Boot Device Manager
  2. Import that PVS Boot ISO into the VMM Library
  3. Execute a PowerShell script

Step 1: Create a PVS Boot ISO

The Provisioning Services Boot Device Manager is a three-dialog wizard that lets you pre-configure the boot environment just like a PXE server would, except you can then write that to a drive or CD-ROM media. The Boot Device Manager is found on the Start menu of any provisioning server at All Programs >> Citrix >> Provisioning Services >> Provisioning Services Boot Device Manager.

I do not want to spend a lot of time discussing the various options or provide a tutorial on this utility; however, I will provide a few pointers. First, be sure to enable the “Citrix PVS Two-Stage Boot Service” and set it to start automatically on any servers you will use as the targets for the ISO image. Second, if you are using Windows 7, be sure to enable the PAE Mode on the second page of the wizard, like this:

Third, be sure to select Citrix ISO Recorder as the boot device (shown below) before burning the ISO image, lest you accidentally wipe out your local hard disk. For a complete guide on using the Boot Disk Manager, see this Citrix Support Article CTX121331.

Step 2: Import the ISO into the SCVMM Library

Take the ISO you created in Step 1 and save it to the folder where the SCVMM library stores are located. I created a new folder called ISOs at the same level as VHDs and placed the ISO in that folder. Next start the SCVMM Administrative Console and go to the Library tab. Select the MSSCVMMLibrary node and click Refresh on the context-menu to add the ISOs to the library as shown here:

Step 3: Execute the PowerShell Script

Next, you can copy the contents of the PowerShell script below and save it to a file called AttachISO.PS1. I realize that I am not yet a PowerShell guru, so I am aware that several optimizations and error checks could be made to this script. Feel free to modify it for your own use. My goal was provide a working example to help with this issue. The PowerShell script below does the following:

  1. Sets the boot order to CD, PXE (Legacy NIC), IDE, Floppy
  2. Copies the ISO image from the library to the VM’s folder
  3. Creates a DVD drive object at the IDE bus 1:0 if no DVD drive is found
  4. Removes any existing ISO and sets the ISO image to the one specified on the command-line
  5. For larger environments, it lets you know how many VMs it has left to process
AttachISO PowerShell Script
# Purpose:      Attach ISO image from VMM Server Library to Guest Virtual Machine
# Date Written: 12 April 2010
# Author:       Paul Wilson (no implied or expressed warranties)
# Usage:        AttachISO [UNC Path to ISO in Library] [VM Name to Match Criteria]

# Check <span class="code-keyword">for</span> the two required arguments and offer command-line assistance <span class="code-keyword">if</span> not found

<span class="code-keyword">if</span> ($args -eq $<span class="code-keyword">null</span> -or $args.Count -lt 2)
{
   write-output <span class="code-quote">"Usage: AttachISO.ps1 UNC_fileName_ISO_File VMNameMatches"</span>
   write-output <span class="code-quote">"Example: .\AttachISO.ps1 "</span><span class="code-quote">"\\SCVMM\MSSCVMMLibrary\ISOs\pvbt.iso"</span><span class="code-quote">" "</span><span class="code-quote">"Desktop"</span><span class="code-quote">" "</span>
   exit 1
}

# Grab the arguments and store them <span class="code-keyword">for</span> later use

$ISOPath = $args[0]
$VMNameMatches = $args[1]

# Get the name of the SCVMM server we are running <span class="code-keyword">this</span> on.
# The VMM server could be passed as a parameter as well.

$VMMServer = Get-VMMServer -Computername <span class="code-quote">"localhost"</span>

# Get the ISO image reference object using the ISO path provided earlier.
# Using the full path guarantees the right object is found.

$ISOImage = Get-ISO -VMMServer $VMMServer | where { $_.SharePath -eq <span class="code-quote">"$ISOPath"</span> }

<span class="code-keyword">if</span> ($ISOImage -eq $<span class="code-keyword">null</span>)
{
   write-output <span class="code-quote">"Unable to find ISO: $ISOPath"</span>
   exit 1
}

# Get the collection of VMs that match the name parameters supplied and output that information

$VMs = Get-VM | where { $_.Name -match <span class="code-quote">"$VMNameMatches"</span> }
<span class="code-keyword">if</span> ($VMs -eq $<span class="code-keyword">null</span>)
{
   write-output <span class="code-quote">"No VMs match the pattern: $VMNameMatches"</span>
   exit 1
}
<span class="code-keyword">else</span>
{
   $LeftToGo = $VMs.Count
   <span class="code-keyword">if</span> ($LeftToGo -eq $<span class="code-keyword">null</span>)
   {
      $matchString = <span class="code-quote">"Only one VM matched the pattern: {0}"</span> -f $VMNameMatches
      $LeftToGo = 1
    }
    <span class="code-keyword">else</span>
    {
      $matchString = <span class="code-quote">"{0} VMs match the pattern: {1}"</span> -f $VMs.Count, $VMNameMatches
    }
    write-output $matchString
}

# This loop goes through each VM found and does the following:
#   1. Sets the boot order to CD, PXE Nic, IDE, Floppy.
#   2. Gets the DVD/CD drive object.
#   3. The script will copy the ISO image from the library to the VM's folder.
#      The copy is part of the Set-VirtualDVDDrive and New-VirtualDVDDrive cmdlets.
#   4. Creates the DVD drive object <span class="code-keyword">if</span> none found and sets it to the ISO.
#   5. Removes any existing ISO and sets the ISO image to the one specified.
#   6. Outputs the number of VMs remaining to process. Added <span class="code-keyword">for</span> large deployments.

foreach ($VM in $VMS)
{
   $LeftToGo = $LeftToGo - 1
   Set-VM -VM $VM -BootOrder CD,PXEBoot,IDEHardDrive,Floppy
   $current_dvd = get-VirtualDVDDrive -VM $VM

   <span class="code-keyword">if</span> ($current_dvd -eq $<span class="code-keyword">null</span> -or $current_dvd.count -eq 0)
   {
      $newDVD = New-VirtualDVDDrive -VM $VM -Bus 1 -LUN 0 -ISO $ISOImage
      $DVDResultMessage = <span class="code-quote">"Created DVD Drive on {0}. {1} VMs left to go."</span> -f $VM.Name, $LeftToGo
   }
   <span class="code-keyword">else</span>
   {
      <span class="code-keyword">if</span> ($current_dvd.Connection -ne <span class="code-quote">"None"</span>)
      {
         set-VirtualDVDDrive -VirtualDVDDrive $current_dvd -noMedia
         set-VirtualDVDDrive -VirtualDVDDrive $current_dvd -ISO $ISOImage
         $DVDResultMessage = <span class="code-quote">"Replaced existing media in DVD Drive on {0}. {1} VMs left to go."</span> -f $VM.Name, $LeftToGo
       }
       <span class="code-keyword">else</span>
       {
         set-VirtualDVDDrive -VirtualDVDDrive $current_dvd -ISO $ISOImage
         $DVDResultMessage = <span class="code-quote">"Successfully attached ISO to the DVD Drive of {0}. {1} VMs left to go."</span> -f $VM.Name, $LeftToGo
       }
    }
    write-output $DVDResultMessage
}

Feel free to add a comment below if you have questions or optimizations that others could benefit from. If you found this information useful and would like to be notified of future blog posts, please follow me on Twitter @pwilson98 or visit my XenDesktop on Microsoft website.

Cross-post