Introduction
So here we are, the final stage - one could say "the final furlong"...
In the previous parts of this article, we've discussed how we configure Packer to deliver a VM and initiate a Windows installation. We've then gone on to discuss the automation of the Windows installation itself. Here, we move onto the last steps of the process - the scripts that configure the VM for use as a VDI desktop and some tips on deploying in Horizon.
Windows Update
Once the basic Windows installation is complete, it will reboot. While Windows was installing, the Packer session is waiting patiently for the VM to return with an IP address and open connection for Windows Remote Management. Once this occurs, the Packer process uses the Packer Windows Update plugin to force Windows to install any outstanding Windows updates, less those labelled Preview.
Although the plugin will manage reboots, as a precaution a further reboot is added in order to ensure that any pending disk transactions are complete.
Packer then moves on to the Stage Scripts.
The Stage Scripts
These are a set of PowerShell scripts that are processed in sequence, separated by reboots to ensure changes are written as needed to disk.
Stage 2 - Certificates and VMware Horizon Agent
The first script installs Certificate Authority signed certificates and VMware Horizon agent. Note that the script is configured to write notes to the screen as the process continues. In conjunction with time stamping provided by Packer, it is possible to monitor activity during the deployment process and identify where a failure occurs.
First, we specify the name of the web server and the CA signed certificates. Note, we also provide the configuration for the Horizon Agent installer.
$ErrorActionPreference = "Stop"
$webserver = "WEBSERVER FQDN"
$url = "http://" + $webserver
$certRoot = "root.cer"
$certIssuing = "issuing.cer"
$HZNAgentinstaller = "VMware-Horizon-Agent-x86_64-2206-8.6.0-20088748.exe"
$HZNAgentlistConfig = "/s /v ""/qn REBOOT=ReallySuppress ADDLOCAL=Core,NGVC,USB,RTAV,ClientDriveRedirection,HelpDesk,PrintRedir,VmwVaudio"""
We then check connectivity to the Web Server and download the content to the root of C: within the desktop. Note that here, we have Certificates in a folder called "certs".
# Verify connectivity
Test-Connection $webserver -Count 1
# Get content
Write-output "======Download Horizon Agent and Root Certificates....."
Invoke-WebRequest -Uri ($url + "/certs/" + $certRoot) -OutFile C:\$certRoot
Invoke-WebRequest -Uri ($url + "/certs/" + $certIssuing) -OutFile C:\$certIssuing
Invoke-WebRequest -Uri ($url + "/agents/" + $HZNAgentinstaller) -OutFile C:\$HZNAgentinstaller
Root and Intermediate Certificates are imported. This is of particular value to App Volumes other other web-based services on the LAN that use private CA signed certificates.
# Import Root CA certificate
Write-output "======Import Root Certificates....."
Import-Certificate -FilePath C:\$certRoot -CertStoreLocation 'Cert:\LocalMachine\Root'
# Import Issuing CA certificate
#Import-Certificate -FilePath C:\$certIssuing -CertStoreLocation 'Cert:\LocalMachine\CA'
The Horizon agent installer executable is then unblocked to allow Windows to run it and then the installer is run as per the parameters declared above.
# Unblock Horizon Agent Installer
Write-output "======Unblock Executables....."
Unblock-File C:\$HZNAgentinstaller -Confirm:$false -ErrorAction Stop
# Install Horizon Agent
Write-output "======Install Horizon Agent....."
Try
{
Start-Process C:\$HZNAgentinstaller -ArgumentList $HZNAgentlistConfig -PassThru -Wait -ErrorAction Stop
}
Catch
{
Write-Error "Failed to install the Horizon Agent"
Write-Error $_.Exception
Exit -1
}
Finally, the binaries are deleted from the root of C:. Packer then carries out a reboot.
# Cleanup...
Write-output "======Clean up Horizon Agent and raw certificate files....."
ForEach ($file in $HZNAgentinstaller,$certRoot) {
Remove-Item C:\$file -Confirm:$false
}
Stage 3 - Additional Agents and Configuration
Stage 3 follows the reboot. This is used to install additional agents, any configuration specific to the build and then carry out the optimization of the image using VMware Guest Operating System Optimization Tool. We start by declaring our web server, as before.
$ErrorActionPreference = "Stop"
$webserver = "WEBSERVER FQDN"
$url = "http://" + $webserver
Where used, we then declare variables for VMware App Volumes and VMware Dynamic Environment Manager.
#Base Horizon - App Volumes and DEM
$AVinstaller = "App Volumes Agent.msi"
$AVServer = "App Volumes Manager FQDN"
$AVlistConfig = "/i ""C:\$AVinstaller"" /qn REBOOT=ReallySuppress MANAGER_ADDR=$AVServer MANAGER_PORT=443 EnforceSSLCertificateValidation=0"
$DEMinstaller = "VMware DEM 2206 10.6 x64.msi"
$DEMlicence = "dem.lic"
$DEMlistConfig = "/i ""C:\$DEMinstaller"" /qn /norestart ADDLOCAL=FlexEngine COMPENVCONFIGFILEPATH=\\App Volumes Manager FQDN\config$\General LICENSEFILE=dem.lic"
We can also decalre any variables for additional components and configuration. This will vary. Here, we include FSLogix as an example. Note we also list components required for the full run of VMware OSOT. This includes an exported Settings JSON file as well as LGPO.exe and sdelete64.exe available from Microsoft SysInternals.
#Optional extras and optimization
$fslogix = "FSLogixAppsSetup.exe"
$fslogixSwitches = "/install /quiet /norestart"
$osot = "VMwareHorizonOSOptimizationTool-x86_64.exe"
$LGPO = "LGPO.exe"
$sdelete64 = "sdelete64.exe"
$osotsettings = "Win11-OneDrive.json"
$OSOTlistconfig = "-o -applyoptimization C:\$osotsettings"
As with Stage 2, we download the content required from the Web Server.
# Verify connectivity
Test-Connection $webserver -Count 1
# Get Content for App Vols and DEM
Write-output "======Download App Volumes and DEM Installers....."
ForEach ($file in $AVinstaller,$DEMinstaller,$DEMlicence) {
Invoke-WebRequest -Uri ($url + "/agents/" + $file) -OutFile C:\$file
}
# Get Content for Extras and OSOT
Write-output "======Download Extras and OSOT Files....."
ForEach ($file in $osot,$osotsettings,$LGPO,$sdelete64,$fslogix) {
Invoke-WebRequest -Uri ($url + "/osot/" + $file) -OutFile C:\$file
}
# Unblock Executables
Write-output "======Unblock Executables....."
ForEach ($file in $AVinstaller,$DEMinstaller,$osot,$LGPO,$sdelete64,$fslogix) {
Unblock-File C:\$file -Confirm:$false
}
We install the VMware App Volumes Agent and VMware DEM Agent as needed.
# Install AppVolumes Agent
Write-output "======Install App Volumes....."
Try
{
Start-Process msiexec.exe -ArgumentList $AVlistConfig -PassThru -Wait
}
Catch
{
Write-Error "Failed to install the AppVolumes Agent"
Write-Error $_.Exception
Exit -1
}
# Install DEM Agent
Write-output "======Install DEM Agent....."
Try
{
Start-Process msiexec.exe -ArgumentList $DEMlistConfig -PassThru -Wait
}
Catch
{
Write-Error "Failed to install the DEM Agent"
Write-Error $_.Exception
Exit -1
}
We then deploy any additional software and configuration required. Here, we include FSLogix as an example. One item to note is that OneDrive is baked into Windows 10 in Single User Mode. In VDI, this should be configured for Shared mode and will need re-installing.
# Install FSLogix
Write-output "======Install FSLogix....."
Try
{
Start-Process "c:\$fslogix" -ArgumentList $fslogixSwitches -PassThru -Wait
}
Catch
{
Write-Error "Failed to run FSLogix"
Write-Error $_.Exception
Exit -1
}
The last part (and quite time consuming) is the Optimization phase for VMware OSOT. This uses a provided JSON file to select relevant settings from the built in template to optimize the VDI image.
#Run OSOT Process
Write-output "======VMware OSOT Optimization....."
Try
{
Start-Process "c:\$osot" -ArgumentList $OSOTlistconfig -PassThru -Wait
}
Catch
{
Write-Error "Failed to run OSOT"
Write-Error $_.Exception
Exit -1
}
As with Stage 2, prior to a reboot, the binaries are cleared up. The exception is the OSOT content which are used in Stage 4, following the reboot.
# Cleanup...
Write-output "======Clean up Installer content....."
ForEach ($file in $AVinstaller,$DEMinstaller,$DEMlicence,$fslogix) {
Remove-Item C:\$file -Confirm:$false
}
Stage 4 - Final Step
This stage carries out the finalization steps of OSOT, releasing IP addresses, deletion of temp files etc. It is also an appropriate location to deploy agents etc that are installed in a "trigger mode" where they don't immediately register, but do so on the following (or more) boot-ups to support VDI deployment. Once OSOT has completed, the contents are cleared up.
$ErrorActionPreference = "Stop"
#Optional extras and optimization
$osot = "VMwareHorizonOSOptimizationTool-x86_64.exe"
$LGPO = "LGPO.exe"
$sdelete64 = "sdelete64.exe"
$OSOTlistconfig = "-f 0 1 2 3 4 5 6 7 8"
$osotsettings = "Win11-OneDrive.json"
# Unblock Executables
Write-output "======Unblock Executables....."
ForEach ($file in $osot,$LGPO,$sdelete64) {
Unblock-File C:\$file -Confirm:$false
}
#Run OSOT Process
Write-output "======VMware OSOT VM Prep....."
Start-Process -Wait "c:\$osot" -ArgumentList $OSOTlistconfig
# Cleanup...
Write-output "======Clean up Installer content....."
ForEach ($file in $osot,$LGPO,$sdelete64,$osotsettings) {
Remove-Item C:\$file -Confirm:$false
}
At the end of this stage, Packer shuts down the virtual machine via vSphere and then takes a snapshot of the image. It is then ready for use in VMware Horizon.
Deploying the Gold Image
There are two events that trigger the deployment of the new Gold Image. The first is configuring a new VMware Horizon Instant Clone Pool, the other is maintenance on a pre-existing Pool.
In the first case, the Instant Clone Pool is deployed as any other would be. Being Windows 11 though, it is important to enable the setting "Add vTPM Device to VMs" to properly meet the needs of Windows 11.
For carrying out maintenance on a pool, simply use the Maintenance function to schedule the update of the Instant Clone pool, selecting the new Gold Image and the relevant snapshot.
Closing Thoughts....
And there you have it - the means to deliver a fresh Windows gold image in a repeatable, consistent manner. By replacing executables stored on the Web Server, it is possible to deploy incremental updates to agents, while the Windows Update Plugin ensures that Windows Updates are applied. This means that Patch Tuesday requires less effort than before to achieve a more reliable result.
Although there's some effort to set this up, once it's in place, deploying new versions of images is much less effort and more reliable than the monthly "...update-snapshot-update-snapshot-update-snapshot" chore.

0 Comments