Windows Build Automation with Packer, PowerShell 2022 Redux

by Owen Reynolds, CTA

The related presentation recording is here: User Share: Windows Build Automation w/Packer, PowerShell 2022 Redux – CUGC (mycugc.org)

Last year, I wrote a long post about using Packer.IO to automate basic VMware shell creation and Win 10 / Win 2019 installation. At that time, I only ended up using the solution for re-builds on my own home lab. This year, I’ve had the need to build golden images for multiple clients, each time, the process was manual and error prone as no automation was used.

In the last week of Nov 2021, I decided to sit down and re-visit my Packer / PowerShell windows build templates.

I’m very happy to share that I’ve got automation in place to deliver a fully built / base optimized / bi-lingual (En/Fr) / Windows patched Windows EFI image in approximately 25 minutes. Last year, the builds were about 10 mins, but didn’t do HAVE of what I have now. Let’s get into it!

For reference, here was the blog post from last year on packer / windows build automation for VMware environments

If you’ve not read it, give it a read, as I won’t be re-reviewing most of the stuff I did in the above post (which was LOOOONG). This new blog post is about the new PowerShell code I wrote to achieve a better level of automation.

The Goal

Building golden images for windows is a bit of a mug’s game. Like anything in the mostly unregulated world of IT, there’s not really an agreed upon standard.

The scripts / config files are on my GitHub here

To start, you will deploy windows with an autounattend.xml. Autounattend.xml files have been around for a while, and you can use them with Packer or MDT or SCCM, or other. The idea is to deliver a Windows build with no prompts. The full structure of the autounattend.xml file is described in my blog post from last year.

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 JSON, 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

As above, you can review my blog post from last year if you want a full primer on Packer / JSON / autounattend.xml usage.

For this blog post, download the required Win 2022 JSON / XML templates from my github here:

https://github.com/getvpro/Build-Packer/tree/master/Config/JSON

https://github.com/getvpro/Build-Packer/tree/master/Config/Autounattend

Start with the JSON file, open it using a text editor (like Notepad3).

Edit line 30 to cover the path to where you’ve got an up-to-date VMwareTools.iso

Edit line 50 to and choose a unique local admin user that will be used for the build process, you can delete it when done, or rename it, but it needs to be the same as is set in the autounattend.xml file you will be editing next.

Edit line 66-79 for your environment.

Next, open the Autounattend.xml file.

CTRL + H to do a search/replace for anything labelled as “CHANGEME”, amend as required for your environment, ensuring you’ve set the username / password the same as the JSON you just edited.

Line 102 can be edited for your local time zone as well.

Line 103 can be edited to amend your preferred computer name.

The scripts

https://github.com/getvpro/Build-Packer/tree/master/Scripts

There are 4 scripts that are called as first logon activities from the autounattend.xml file.

You will need to download them each to the c:\Program Files\Packer\Scripts folder created earlier.

Lastly, download the Packer start build script, essentially a wrapper that starts packer/then uses PowerCLI to connect to vCenter / start the VM once Packer has done its initial provisioning.

https://github.com/getvpro/Build-Packer/blob/master/Scripts/Start-PackerBuild-Win2022.ps1

You will need to install VMWare PowerCLI to use the script.

Open PowerShell as an admin, and run

Install-Module -Name VMware.PowerCLI -AllowClobber -force

..if you get a Nuget package manager can’t be found BS error, run the following:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Then the above again.

With the file’s downloaded, the json/autounattend.xml edited for your environment, you’re ready to start the build.

Build process

Launch the Start-PackerBuild-Win2022.ps1, it will ask for the VM name you set in the JSON file for your new Packer build VM, your vCenter name and credentials to connect to it. Record relevant vCenter info to be used to power on the VM once packer has done it’s first tasks.

The Start-FirstSteps.ps1 script 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 businesses 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 10 1909 / 20H1 / 21H1 / Server 2016 / Server 2019 etc.

As I’m using Server 2022, Microsoft doesn’t include the various language packs on the ISO, it’s a separate download that isn’t titled as Microsoft says it should be. I got it via my.visualstudio.com

I’m probably breaking some kind of law by hosting the Fr-CA .cab on my github, but I don’t care, come at me, MS bros 🙂

The script downloads the multi-part zip and extracts it, but if you want it, the link to the Fr-CA cab is here.

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 update, the second will monitor its 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 that there are no more updates to apply, the related scheduled tasks will be disabled, and 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 your Windows build done, it’s time to install some apps! For that, you’ll want to switch over to my good friend/fellow Canadian CTA/co-worker Jonathan Pitre’s git hub, HERE.

The Packer build installs the pre-reqs for most of 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 and let it rip!

I will update this blog post once more when I’ve got my Packer JSON config files updated to HCL.

Have a great day and happy automating 🙂

One comment

Leave a Reply