Part 1: PowerShell Pre-Reqs, Functions inside Remote Sessions
Part 2: Create a Sysprepped Template & Create a Group of VMs With 1 Click
Part 3: Set a Static IP & Join Machines to the Domain
Part 4: Remotely Install XenDesktop Software
Part 5: Remotely Add StoreFront & Delivery Controllers
If you are beginning with a XenDesktop deployment on Hyper-V and need a Windows Image to be used as a template, this script will help automate its creation. Without System Center Virtual Machine Manager, Hyper-V does not support creating a template to spin up additional virtual machines. The goal of this blog post is to create a Sysprepped Windows 2012 R2 image that will be used as the template with a single click. The Sysprepped image will be replicated by copying and renaming the file to create the machines needed to build out a XenDesktop environment. On a side note, be sure to check out this great Hyper-V Planning Guide blog series from Consulting.
Starting Point: The Pre-Requistes
To begin using the code below, a few items must first be in place:
- A physical server with the Hyper-V role installed and configured.
- The scripts below assumes a virtual switch on Hyper-V named “InternalNetwork” is available.
- An Active Directory deployment with DHCP/DNS must be in-place.
- The script is meant to be executed locally from a Hyper-V server.
- Besides that, no other resources such as System Center Virtual Machine Manager or SQL Server are assumed to be already built.
High Level PowerShell Overview
The following steps will are detailed below:
- Create a Windows 2012 R2 Image from an ISO. Use an autounatended.xml file to allow the installation to happen without any user intervention.
- Once Windows is installed, the machine will receive a temporary IP address from DHCP. This will allow remote access for PowerShell.
- The newly created Windows VM will then be Sysprepped using an unattend.xml file and then shutdown.
- Once the VM is shutdown, it will be replicated by coping and renaming the file based on the VMs that need to be created.
Define VMs to be Created
To define what VMs should be created, a multi-dimensional array of the VMs will be passed to the script. Each VM in the array is separated by parentheses and includes a set of values. Below are what each value in the array correlate to:
- LogicalName: The name the VM will be given in Hyper-V. The value will be used to reference the VM in later parts of the script.
- HostName: The Hostname that will be used inside Windows.
- Static IP: The static IP address that will be given to the VM.
- VM Location: The Hyper-V server hostname where the VM is located.
- vCPUs: The number of Virtual CPUs the VM should be given.
- Memory: The amount of memory that should be given to the VM.
Example:
$VMs =@ (‘DeliveryController1′,’XDC-01′,’172.1.1.10′,’server1′,’2′,’4’),(‘DeliveryController2′,’XDC-02′,’172.1.1.11′,’server1′,’2′,’4’),(‘SQLServer1′,’XDSQL-01′,’172.1.1.11′,’server1′,’2′,’4’)
Copy Function
This function was created since the native “copy-item” command in PowerShell does not support the Credential parameter. This means that the account running the script will be used when copying files to a remote machine. To get around this limitation, the “net use” command is used to authenticate with the remote machine. This allows the copy-item to use the credentials specified to authenticate to the network share on the remote host.
$username = "administrator" $password = "mypassword" function copy_remote($source, $dest) { net use $dest $password /USER:$username Copy-Item -Path $source -Destination $dest –Recurse -Force net use * /delete /y }
VM State Function
This function will be used to verify if a VM on Hyper-V is powered On or Off. In this case, it will be used to verify that the sysprepped VM is powered off before it is copied.
function vm_state($a, [ref]$vmstate) { $vmstate.value = (Get-VM –name $a).State }
Function To Determine VM IP
Since using PowerShell Remoting will be used to perform the Sysprep operation, the DHCP IP address of the Windows must be know. The function below will query the Hyper-V server and loop until it returns a valid IPv4 address.
function network_online($a, $b, [ref]$ipv4) { # Loop until VM has a real IP address do { $VMIP = (Get-VMNetworkAdapter -VMName $b -Computer $a).IpAddresses write-host "." -NoNewline -ForegroundColor "Red" Start-Sleep -s 2 } until (($VMIP -ne $null) -AND ($VMIP[0] -notmatch "169.")) $ipv4.value = $VMIP[0] }
Convert Memory Size Function
In the $VMs array discussed earlier, one of the values assigned is the memory size in GB that we want to give each VM . To convert this value so that it is readable to the PowerShell code that creates the VM, this PowerShell function found here will be used. It allows the value to be easily converted to the memory size that will be used.
function Convert-Memsize ( [string] $memsize ) { $memsize = $memsize.ToUpper() $result = 0 switch ($memsize.ToUpper().Substring($memsize.Length-2)) { "KB" { $result = [Int64]::Parse($memsize.Replace("KB",""))*1024 } "MB" { $result = [Int64]::Parse($memsize.Replace("MB",""))*1024*1024 } "GB" { $result = [Int64]::Parse($memsize.Replace("GB",""))*1024*1024*1024 } "TB" { $result = [Int64]::Parse($memsize.Replace("TB",""))*1024*1024*1024*1024 } default { return $memsize } } return [int64] $result }
Create the Initial VM
The function below creates a VM named WindowsMaster which will be used as the template for all the VMs in the XenDesktop build. Using an autounattended.xml file such as this one placed on a virtual floppy file, Windows will be allowed to complete it’s installation without any under intervention. This file sets items such as the partition size and local administrator password.
function initial_vm() { # Variables $SRV1 = "WindowsMaster" # Name of VM $SRAM = 4GB # RAM assigned $SRV1VHD = 60GB # Size of Hard-Drive $VMLOC = "C:\VMs" # Location of the VM and VHDX files $NetworkSwitch1 = "InternalNetwork" # Name of the Network Switch $WSISO = "C:\Deploy\ISOs\windows2012_r2.iso" # Windows Server 20012 R2 ISO $WSVFD = "C:\Deploy\unattend\unattend.vfd" # Windows Server 20012 R2 Virtual Floppy Disk with autounattend.xml file # Create VM Folder and Network Switch MD $VMLOC -ErrorAction SilentlyContinue $TestSwitch = Get-VMSwitch -Name $NetworkSwitch1 -ErrorAction SilentlyContinue; if ($TestSwitch.Count -EQ 0){New-VMSwitch -Name $NetworkSwitch1 -SwitchType External} # Create Virtual Machines New-VM -Name $SRV1 -Path $VMLOC -MemoryStartupBytes $SRAM -NewVHDPath $VMLOC\$SRV1.vhdx -NewVHDSizeBytes $SRV1VHD -SwitchName $NetworkSwitch1 # Configure Virtual Machines Set-VMDvdDrive -VMName $SRV1 -Path $WSISO Set-VMFloppyDiskDrive -VMName $SRV1 -Path $WSVFD Start-VM $SRV1 Write-Host "VM Installation Beginning ..." }
Sysprep the VM
Once the WindowsMaster VM installation has completed, the VM must be Sysprepped before it is copied to be used by the other machines that will makeup the XenDesktop environment. This function begins by waiting until the WindowsMaster VM receives an IP address from DHCP and then returns the value to the $IPv4 variable. Using that IP address, the function copies a Sysprep unattend.xml file to the local and then performs the Sysprep operation. The unattend.xml file include information such as giving the machine a random hostname. Once the Sysyprep operation is completed, the VM will be shutdown.
function sysprep() { #Set Variable Set-Variable -Name ipv4 Sleep 5 #Call Function to Determine if VM is Online and then determine it's IP Address Write-host "Waiting for VM to Finish Installation and Shutdown ..." # Return the VM's IP Address network_online server1 WindowsMaster ([ref]$ipv4) write-host "VMIP: $IPv4 ..." Sleep 40 # Copy Unattended File copy_remote c:\Deploy\unattend\unattend.xml -Destination \\$ipv4\c$\unattend.xml Write-Host Starting Sysprep Process... # Execute Sysprep on Template VM Invoke-Command -ComputerName $ipv4 -credential $cred -ScriptBlock { # Install .NET 3.5 (Needed later by SQL Server) dism /online /enable-feature /featurename:NetFX3 /all /Source:d:\sources\sxs /LimitAccess Start-Process -FilePath "c:\windows\system32\sysprep\sysprep.exe" -Wait -ArgumentList "/oobe /generalize /shutdown /unattend:c:\unattend.xml" } }
Create VMs Functions
Now that we have Sysprepped Windows machine, we can simply copy the VHDX file and rename it to be used for the machines that will be part of the XenDesktop deployment. The following function begins with waiting for the WindowsMaster VM to be shutdown and then proceeds to loop through the $VMs array and creates a copy of the WindowsMaster.vhdx file for each VM in the array. After the copy is created, a new VM on Hyper-V is created using the Sysyprepped Windows image which has been copied. Finally, the vCPUs for each VM are specified and the VM is set to start.
function create_vms() { Set-Variable -Name vmstate # Call function to determine VMs state vm_state WindowsMaster ([ref]$vmstate) write-host state: $vmstate # Loop to Wait for WindowsMaster VM to Shutdown do { write-host "." -NoNewline -ForegroundColor "Red" vm_state WindowsMaster ([ref]$vmstate) Start-Sleep -s 2 } while ($vmstate -ne "Off") #Return the VM state write-host $vmstate Sleep 5 $NetworkSwitch1 = "InternalNetwork" # Name of the Network Switch # Copy Syspreped VM foreach ($z in $VMs) { ') "Creating VHD for $($z[0]) VM ...`n" Copy-Item -Path "c:\VMs\WindowsMaster.vhdx" -Destination "\\$($z[3])\c$\VMs\$($z[0]).vhdx" } # Create Virtual Machines foreach ($z in $VMs) { $memory = Convert-Memsize -memsize "$($z[5])GB" "Creating $($z[0]) VM ..." New-VM -Name $z[0] -ComputerName $z[3] -MemoryStartupBytes $memory -VHDPath "c:\VMs\$($z[0]).vhdx" -SwitchName $NetworkSwitch1 } # Set vCPUs foreach ($z in $VMs) { SET-VMProcessor –VMName $z[0] –count $z[4] -ComputerName $z[3] } # Start the VMs foreach ($z in $VMs) { Start-VM $z[0] -ComputerName $z[3] "Starting $($z[0]) VM ..." Sleep 80 } Sleep 200 }
Executing the Functions
Now that all our functions are created, we can set them to run sequentially by calling the individual functions at the end of the script:
# Create Initial Template VM
initial_vm
# Create the Sysprep Master Template
sysprep
# Create the Virtual Machines
create_vms
Depending on what VMs are entered in the array, the VMs will appear in Hyper-V as shown below. Of course the NetScaler VMs are not created from the sysyprepped Windows machine. I’ll leave it to you to figure out how to spin up the NetScaler machine with the same type of method.
Roger LaMarca – Senior Consultant
Worldwide Consulting
Virtual Desktop Handbook
Project Accelerator
Follow @rogerlamarca