Intro
Hot on the heels of the CUGC presentation I did 2 weeks back with my co-worker / friend Jonathan Pitre, I took some time this week to update my Packer config files for Windows 11. I have a client who wants to do a Citrix POC with Windows 11, so, let’s brain dump all the stuff while it’s still fresh
Btw, the related blog post on Win 2022 packer is here , and the YouTube video of the actual CUGC presentation is here
Goals and pre-reqs
The goals are the same as before, deliver a fully built windows golden (reference) image with as few clicks as possible to a VMware vCenter cluster. In my case, my entire build process is started / managed / logged by PowerShell. The Packer.exe is called via a base script, that’s used to power on the newly created Packer VM after the initial packer provisioning is done. I will cover the setup steps in the next section.
The pre-reqs for this build are as follows:
- Download a non-trial version of Windows 11 from your Microsoft portal, I use my.visualstudio.com . Once it’s downloaded, upload it to your VMware datastore and give it a human-readable name, packer config files are case-sensitive. I’d suggest win_11_month_year_business.iso
- Upload a recent VMware tools ISO to your datastore, again, keep the name simple. I included a sample name in my packer HCL config file. I’d suggest vmt_11_3_x.iso
- Run through the following to setup your vCenter as a key provider, this is a pre-req for the virtual TPM requirement of Windows 11
Setup
On your windows machine create a directory structure as shown below, starting with c:\Program Files\Packer
Within the config sub-folder create two folders, one called HCL, one called autounattend
Files will be downloaded to each in further steps
Download / extract packer from packer.io to the c:\Program Files\Packer folder you created
Set a system environment variable to c:\Program Files\Packer


The config files
Download the required Win 11 HCL / XML templates from my github HERE:
Start with the HCL file, open it using a text editor (like Notepad3)
Edit line 31 to cover the path to where you store the Windows 11.ISO downloaded in the previous section
Edit line 122 to point to the datastore path where you uploaded a recent VMwareTools.iso
CTRL + H to do a search/replace for anything labelled as “CHANGEME”, amend as required for your environment
Download the Win 11 Autounattend.xml sample file from my GITHUB HERE
Open the file in NotePad3 or a similar editor, use CTRL + H to search and replace through the file for any references to CHANGEME. These are mostly references to local admin name/password, ensure you set the username / password the same as the HCL you just edited
I converted my JSON legacy template to HCL this week. The newly updated HCL has a call to download plug-ins via the following command which you need to run to download the vSphere plug-ins to your %appdata% directory
open CMD as admin
CD to c:\Program Files\Packer
packer init “C:\Program Files\Packer\config\HCL\Win11_EFI_Enterprise.json.pkr.hcl”
This is required in order for the vTPM variable on line 116 of the Packer HCL to work
You’re just about done with the download/setup of your config files, one last set of files into a new directory: c:\Program Files\Packer\Scripts\pvscsi
Within the new directory, you will need 4 files from the windows VMware tools ISO. They are buried deeeeep!
With the ISO mounted on your windows machine, find them here, changing the “D:” drive accordingly:
D:\Program Files\VMware\VMware Tools\Drivers\pvscsi\Win8\amd64

The requirement for these files, is to ensure that your Win 11 EFI install detects the VMware paravirtual SCSI controller during the install. Note: I did not have to follow this process on my Win 10 / Win 2022 EFI based installs. TBH, I’m not 100% why this is now a requirement, but will update my blog post If i find out, else, if you know, let me know in the comments

Download the final script, to c:\Program Files\Packer this script is used to start the packer build
If you don’t already have it installed, install VMware PowerCLI , it’s required for the above script
Starting the build
open Powershell as admin, then CD to c:\Program Files\Packer
Start the script: .\Start-PackerBuild
You will be asked to choose the OS , 1 for Win 11, 2 for Windows 2022
You’ll be asked for a VM name, this should be the same as is set in the related JSON/HCL file
You will be asked for your vCenter instance name
You will be asked for vCenter credentials
Assuming the VM doesn’t exist already, the Packer build will start, if you choose a VM name that exists in your environment already, the script will exit. This is done for safety reasons. In my lab, I run packer with a -force switch to delete the existing VM, I wouldn’t recommend this in prod, you might have a co-worker also creating / testing VMs on your vCenter environment, and will delete their VM if you use ‘-force’ when launching Packer
Watching the build
The base windows install will proceed in En-US. All reboots / logins / prompts / selecting the disk, putting in the volume license key etc are automated
Note: the VLK can be amended later via the normal method:
Windows > Activation > Change key

The first script to run on the first reboot is the Install-VMwareTools.ps1 script, this needs to be done to enable Packer to pickup the IP of the VM shell it’s created on your vCenter infra
Once VMware tools is installed, the Start-FirstSteps.ps1 script will run. It contains the most important steps, as it downloads 2 more scripts / scheduled tasks from my github
The actual order of execution of all these scripts is shown in the following screenshot:

I live in Montreal, Quebec. A lot of business need to have both of Canada’s official languages installed on their systems to maintain compliance with these folks https://www.oqlf.gouv.qc.ca/accueil.aspx. These lads can levy fines of up to $7000 Cdn for not having Fr-Ca support on a computer system. This includes Fr-CA physical keyboards. With this build automation, my end of it is the Fr-Ca language pack.
My French speaking/listening skills aren’t great, but I can read it well enough, and can certainly navigate windows when it’s running in Fr-CA, enough to automate and configure!
The Start-FirstSteps.ps1 script automates the entire process of download the language cabs for Fr-CA from my github, installing it via Add-WindowsPackage , as well as the last part, to actually add Fr-CA as a display language you can see from the notification area of the task bar. Of the new code I added to the Start-FirstSteps.ps1 script from last year to now, this part took quite a while. Lang packs are per OS, so you need to DL the correct one for your exact OS build: Win 11 / Win 10 1909 / 20H1 / 21H1 / Server 2016 / Server 2019 etc.
I’m probably breaking some kind of law by hosting the Fr-CA .cab on my github, but I don’t care, come @ my , MS bros. You owe me for the Windows updates nightmares over the years. Apparently, from your non-existing QA !
/end rant
The script downloads the multi-part zip and extracts it
If you want to be MORE complaint and have access via your employer to my.visualstudio.com, you should download the entire pack and adjust the lines in the script that download the Fr-Ca.cab
if you don’t need Fr-Ca support in your image, open the related example Autounattend.xml from my github, and search for the following:
CMD /c reg.exe ADD “HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment” /v FrenchCaLangPack /t REG_SZ /d 1 /f
Change the /D 1 to /D 0
Once the Fr-Ca language pack is installed, the machine will reboot, and start processing two custom scheduled tasks. The first scheduled task will start windows updates, the second will monitor it’s progress. Of all the new code I’ve this packer v2 project, this piece was the most challenging and frustrating. For about 3 years, i’ve been happy using the Powershell module PSWindowsUpdate to automate installation of windows updates on my lab, and for some clients. However, during regression testing of my packer v2 build , I was NOT able to get PSWindowsUpdate to successfully apply windows updates 100% of the time. It worked well, but would get stuck during the download process about 50% of the time, which is not usable for an automated build. I tried all kinds of work-arounds , but could never do better than 50%. As such, I decided to seek out an alternative method, and found one that’s actually built into windows! It’s an exe I’d never heard of before called UsoClient.exe
The exact line I’m using is UsoClient.exe StartInteractiveScan
This opens the familiar windows update UI we use to patch windows interactively, however, with the “StartInteractiveScan” option selected a scan is done, and patches start applying right away

The imported scheduled tasks run in the system context, the default behavior for running a PS script via a scheduled task in the system context means the logged in user won’t see the output of the script or even the windows update UI, which isn’t ideal.
So, to present this info to the logged in user, my co-worker Jon Pitre recommended I have a look at an SCCM component called ServiceUI.exe. Launching Powershell via this exe will show the output to the current user, neat! In this way, the various stages of the packer build can be shown to the user. There are several auto logons set for the build to cover the reboots after windows updates have applied

Once it’s determined by the Monitor-WinUpdates.ps1 script running as a scheduled task that there are no more updates to apply, the related scheduled tasks will be disabled. A final window to the user will be shown that indicates the total build time, and that you’re ready to join the machine to your AD domain or run whatever post base build actions you want to for your client environment

With the build done, it’s time to install some apps! For that, you’ll want to switch over my good friend/fellow Canadian CTA/co-worker Jonathan Pitre’s git hub, HERE
The packer build installs the pre-reqs for most of the Jon’s app install scripts: nuget, Winget, Powershell application deploy toolkit, evergreen and more, so, you would just need to choose the apps you need for your golden image and let it rip!
I hope this blog post was useful. I banged it out on a VERY cold Sunday here in Montreal: -18 degrees centigrade 🧊🥶
Have a nice day 😀
Owen