In an effort to finish publishing the PowerShell scripts I use for scalability testing, today’s GenVMs.ps1 PowerShell script demonstrates how to create basic virtual machines on Microsoft Hyper-V 2008 R2 hosts through the SCVMM PowerShell interface. The exciting part about today’s script is that when combined with the scripts I have previously released on this blog, it can be used to almost completely replace all the functionality of the XenDesktop Setup Wizard. The only thing left would be to create the Desktop Group, (which is on my list to automate but I have not got to that yet), in the Delivery Services Console.

This PowerShell script does the following:

1. Gets the next available MAC address from the Hyper-V address pool.
2. Creates a default profile with a single processor, 1GB RAM, and boot order.
3. Creates a VM using the supplied base name with a legacy network adapter and a DVD drive.

GenVMs Syntax

Usage: GenVMs.ps1 VMTargetHost VMBaseName NetworkName LocalVMStoragePath NumberToCreate StartingAt

Where:
VMTargetHost= The Hyper-V host that will have the VMs created on it.

VMBaseName= The base name for the virtual machines created. This name will have 00-99 appended to it.

NetworkName= The name of the Hyper-V network to assign to the Legacy network adapter.

LocalVMStoragePath= The path local to (or appears local to in the case of a CSV) the VMTargetHost. This is the path where the VM configuration files and the .BIN memory save-state file will be created.

NumberToCreate= The number of virtual machines to create. Must be between 1 and 99.

StartingAt= The starting number for the virtual machine incremental counter. This number plus the NumberToCreate should not exceed 99.

Example: .\GenVMs.ps1 “HOST01” “HVDesktop” “External” “E:\Hyper-V” 50 1

The example above will create desktops named HVDesktop01 – HVDesktop50 on HOST01 and the VM configuration files will be stored in HOST01’s E:\Hyper-V folder.

GenVMs PowerShell Script
# Purpose:    Generate up to 99 virtual machines using the command-line parameters supplied
#             <span class="code-keyword">for</span> customization of the <span class="code-keyword">new</span> virtual machine.
# Date:       22 July 2010
# Author:     Paul Wilson (no implied or expressed warranties)
# Notes:      The script only creates VMs on a single host. To create VMs on multiple hosts
#             run multiple instances of the script from a batch file or create an <span class="code-keyword">outer</span> loop.
#             The LocalVMStorage path must exist or the script fails. I have not added any
#             data validation checks to the script.

# Parse the command-line and verify the 6 required parameters are present, <span class="code-keyword">if</span> not display usage info
<span class="code-keyword">if</span> ($args -eq $<span class="code-keyword">null</span> -or $args.Count -lt 6)
{
    write-output <span class="code-quote">"Usage: GenVMs.ps1 VMTargetHost VMBaseName NetworkName LocalVMStoragePath NumberToCreate StartingAt"</span>
    write-output <span class="code-quote">"Example: .\GenVMs.ps1 "</span><span class="code-quote">"HOST01"</span><span class="code-quote">" "</span><span class="code-quote">"HVDesktop"</span><span class="code-quote">" "</span><span class="code-quote">"External"</span><span class="code-quote">" "</span><span class="code-quote">"E:\Hyper-V"</span><span class="code-quote">" 50 1 "</span>
    exit 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>

# Place the command-line parameters into named variables <span class="code-keyword">for</span> later use.
$VMHost = $args[0]
$VMBaseName = $args[1]
$NetworkName = $args[2]
$VMPath = $args[3]
$VMCount = $args[4]
$StartCount = $args[5]
$EndCount = $StartCount + $VMCount - 1

<span class="code-keyword">for</span> ($i=$StartCount; $i -le $EndCount; $i++)
{

    # Create a job group id to link the items together and create them as a group with the New-VM command

    $JobGroupID = [<span class="code-object">System</span>.Guid]::NewGuid().ToString()

    # Get a MAC Address from the pool of available MAC addresses on the server. (Alternatively a MAC address could be assigned here.)

    $PooledMACAddress = New-PhysicalAddress -Commit

    # Get a network object <span class="code-keyword">for</span> creating the network adapters

    $VNetwork = Get-VirtualNetwork | where {$_.Name -match $NetworkName -and $_.VMHost -match $VMHost}

    # Create a Virtual Legacy Network Adapter required <span class="code-keyword">for</span> PXE booting with Provisioning Services

    New-VirtualNetworkAdapter -JobGroup $JobGroupID -PhysicalAddressType Static -PhysicalAddress $PooledMACAddress -VirtualNetwork $VNetwork

    # In <span class="code-keyword">case</span> PXE booting will not be required or a second synthetic adapter the following line can be uncommented

    # New-VirtualNetworkAdapter -JobGroup $JobGroupID -PhysicalAddressType Dynamic -Synthetic -VirtualNetwork $VNetwork

    # Create a virtual DVD

    New-VirtualDVDDrive -JobGroup $JobGroupID -Bus 1 -LUN 0

    # Create a <span class="code-keyword">new</span> Hardware Profile <span class="code-keyword">for</span> a XenDesktop and set the <span class="code-keyword">default</span> values or use the existing profile.

    $HWProfile = Get-HardwareProfile | where {$_.Name -eq <span class="code-quote">"XD4Profile"</span>}

    <span class="code-keyword">if</span> ($HWProfile -eq $<span class="code-keyword">null</span>)
    {
        write-output <span class="code-quote">"Hardware profile not found. Creating a <span class="code-keyword">default</span> profile."</span>
        $HWProfile = New-HardwareProfile -Owner <span class="code-quote">"XD4\Administrator"</span> -Description <span class="code-quote">"Hosted XenDesktop"</span> -Name <span class="code-quote">"XD4Profile"</span> -CPUCount 1 -MemoryMB 1024 -BootOrder PXEBoot,IDEHardDrive,CD,Floppy
    }

    # Create the Virtual Machine and assign the VM Name. This only works up to 99 virtual machines.
    <span class="code-keyword">if</span> ($i -lt 10)
    {
        $VMName = <span class="code-quote">"{0}0{1}"</span> -f $VMBaseName, $i
    }
    <span class="code-keyword">else</span>
    {
        $VMName = <span class="code-quote">"{0}{1}"</span> -f $VMBaseName, $i
    }

    New-VM -VMMServer $VMMServer -Name $VMName -VMHost $VMHost -Path $VMPath -HardwareProfile $HWProfile -JobGroup $JobGroupID -RunAsynchronously -RunAsSystem -StartAction NeverAutoTurnOnVM -StopAction TurnOffVM
}

As mentioned earlier, this is a basic script and is meant to provide an example of how to create virtual machines on Hyper-V. This script could easily be modified to create VMs with different CPU/RAM requirements or even include the write-cache drive for Provisioning Server implementations as described in my previous blog PowerShell Scripts for XenDesktop Part 2.

In fact, if you run multiple instances of GenVMs.ps1 followed by copyVHD.ps1 from Part 2 the machine creation can be run in parallel (rather than serially) significantly reducing the deployment time over the XenDesktop Setup Wizard. Once the machines are created, use either GenPVSFile.ps1 from Part 3 or GenPVSMCLI.ps1 from Part 4 to load the machine and MAC address information into the Provisioning Services database. The final step is to return to the Delivery Services Console and create the desktop group.

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.