Automated Windows Server 2022 Packer Build
As Windows Server 2022 Preview is now officially released, I wanted to get my automated build of the Preview release in the home lab and ready to go for testing the newest Windows Server operating system. With this being said, one of the recent tasks I have been working on is creating a home lab Packer build to quickly spin up Windows Server 2022 clones as needed for testing.
***Update*** I have updated the post and code examples to include the new HCL2 template format.
Quick Hashicorp Packer tutorial
As just a quick overview, Hashicorp Packer is a self-contained executable like Terraform that allows producing quick and easy operating system builds across multiple platforms. Using Packer and a couple of JSON files, you can quickly spin up fully automated operating system images and convert these to templates in the case of VMware vSphere for use in your clone process that you may use with Terraform.
With Packer, you can even use it to schedule fresh builds of your operating systems used in production so you can always have an up-to-date and fully patched operating system ready to go at a moment’s notice. This helps to promote the DevOps style of operating your infrastructure so you can have “cattle instead of pets” in your environment.
Automated Windows Server 2022 Packer Build
Let’s look at the automated Windows Server 2022 Packer Build and see how you can easily put together a template for cloning off new Windows Server 2022 virtual machines. What files are needed? The files and versions I am using at the time of this writing are as follows:
Outside of downloading both Packer and Windows Server 2022 Preview build, you will need the following files:
- variables.pkr.hclย โ houses the variable declaration blocks and any defaults
- windowsserver2022.auto.pkrvars.hclย โ houses the variable values you want to define. What is the significance of theย autoย in the file? With the auto appended in the front of the pkrvars.hcl extension, Packer knows to include the file with the build run automatically.
- WindowsServer2022.json.pkr.hclย โ the Packer build file
- Answer file – Generated from Windows System Image Manager (SIM) or copy mine below
- Custom script file(s) – optional
Other considerations and tasks you will need to complete:
- Copy the Windows Server 2022 ISO file to a vSphere datastore
- Copy the windows.iso VMware Tools installer to a vSphere datastore
Now, let’s take a look at the three main files that you need to pull off an automated Windows Server 2022 Packer Build.
Windows Server 2022 Answer file for the automated Packer Build
Like other automated approaches to installing Windows Server, the automated Windows Server 2022 Packer build requires an answer file to provide answers to the GUI automatically and other installation prompts that you normally see in a manual installation of Windows Server. Below is the Windows Server 2022 answer file that I came up with that works in my home lab environment.
A few things to note about the answer file below:
- Replace the key with the server key you want to use for Windows Server 2022
- Replace the passwords below that are listed for auto-login and the Administrator account
- This answer file also enables RDP
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="windowsPE">
<component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SetupUILanguage>
<UILanguage>en-US</UILanguage>
</SetupUILanguage>
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
<component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DiskConfiguration>
<Disk wcm:action="add">
<CreatePartitions>
<CreatePartition wcm:action="add">
<Type>EFI</Type>
<Size>512</Size>
<Order>1</Order>
</CreatePartition>
<CreatePartition wcm:action="add">
<Extend>false</Extend>
<Type>MSR</Type>
<Order>2</Order>
<Size>128</Size>
</CreatePartition>
<CreatePartition wcm:action="add">
<Order>3</Order>
<Type>Primary</Type>
<Extend>true</Extend>
</CreatePartition>
</CreatePartitions>
<ModifyPartitions>
<ModifyPartition wcm:action="add">
<Format>FAT32</Format>
<Order>1</Order>
<PartitionID>1</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Order>2</Order>
<PartitionID>2</PartitionID>
</ModifyPartition>
<ModifyPartition wcm:action="add">
<Format>NTFS</Format>
<Label>Windows</Label>
<Order>3</Order>
<PartitionID>3</PartitionID>
</ModifyPartition>
</ModifyPartitions>
<DiskID>0</DiskID>
<WillWipeDisk>true</WillWipeDisk>
</Disk>
</DiskConfiguration>
<ImageInstall>
<OSImage>
<InstallFrom>
<MetaData wcm:action="add">
<Key>/IMAGE/NAME</Key>
<Value>Windows Server 2022 SERVERSTANDARD</Value>
</MetaData>
</InstallFrom>
<InstallTo>
<DiskID>0</DiskID>
<PartitionID>3</PartitionID>
</InstallTo>
<WillShowUI>OnError</WillShowUI>
<InstallToAvailablePartition>false</InstallToAvailablePartition>
</OSImage>
</ImageInstall>
<UserData>
<AcceptEula>true</AcceptEula>
<ProductKey>
<WillShowUI>Never</WillShowUI>
<Key>VDYBN-27WPP-V4HQT-9VMD4-VMK7H</Key>
</ProductKey>
</UserData>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<TimeZone>Central Standard Time</TimeZone>
</component>
<component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RunSynchronous>
<RunSynchronousCommand wcm:action="add">
<WillReboot>Always</WillReboot>
<Path>a:vmtools.cmd</Path>
<Order>1</Order>
</RunSynchronousCommand>
</RunSynchronous>
</component>
<component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<fDenyTSConnections>false</fDenyTSConnections>
</component>
<component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<FirewallGroups>
<FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
<Active>true</Active>
<Group>Remote Desktop</Group>
<Profile>all</Profile>
</FirewallGroup>
</FirewallGroups>
</component>
<component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SecurityLayer>2</SecurityLayer>
<UserAuthentication>1</UserAuthentication>
</component>
<component name="Microsoft-Windows-ServerManager-SvrMgrNc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DoNotOpenServerManagerAtLogon>true</DoNotOpenServerManagerAtLogon>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<AutoLogon>
<Password>
<Value>secretpassword</Value>
<PlainText>true</PlainText>
</Password>
<LogonCount>2</LogonCount>
<Username>Administrator</Username>
<Enabled>true</Enabled>
</AutoLogon>
<FirstLogonCommands>
<SynchronousCommand wcm:action="add">
<Order>1</Order>
<CommandLine>powershell -ExecutionPolicy Bypass -File a:\setup.ps1</CommandLine>
<Description>Enable WinRM service</Description>
<RequiresUserInput>true</RequiresUserInput>
</SynchronousCommand>
</FirstLogonCommands>
<UserAccounts>
<AdministratorPassword>
<Value>secretpassword</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
</UserAccounts>
</component>
</settings>
<cpi:offlineImage cpi:source="wim:c:/wims/install.wim#Windows Server 2022 SERVERDATACENTER" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>
The variables.pkr.hcl variables file
variable "cpu_num" {
type = string
default = ""
}
variable "disk_size" {
type = string
default = ""
}
variable "mem_size" {
type = string
default = ""
}
variable "os_iso_path" {
type = string
default = ""
}
variable "vmtools_iso_path" {
type = string
default = ""
}
variable "vsphere_compute_cluster" {
type = string
default = ""
}
variable "vsphere_datastore" {
type = string
default = ""
}
variable "vsphere_dc_name" {
type = string
default = ""
}
variable "vsphere_folder" {
type = string
default = ""
}
variable "vsphere_host" {
type = string
default = ""
}
variable "vsphere_password" {
type = string
sensitive = true
}
variable "vsphere_portgroup_name" {
type = string
default = ""
}
variable "vsphere_server" {
type = string
default = ""
}
variable "vsphere_template_name" {
type = string
default = ""
}
variable "vsphere_user" {
type = string
default = ""
}
variable "winadmin_password" {
type = string
default = ""
sensitive = true
}
variable "vm_disk_controller_type" {
type = list(string)
description = "The virtual disk controller types in sequence. (e.g. 'pvscsi')"
default = ["pvscsi"]
}
The Windows Server 2022 auto.pkrvars.hcl variables file
With HCL2, Packer has changed the structure of the Packer files so that you can, like Terraform, split off the variable declarations from the actual values set in for the variables. As you see below, the pkvars.hcl variables file contains all the variable values for your vSphere environment to build the virtual machine template.
- Name windowsserver2022.auto.pkrvars.hcl
vsphere_server = "vcsa.cloud.local"
vsphere_user = "[email protected]"
vsphere_password = "secretpassword"
vsphere_template_name = "Win2022clone_hcl2test"
vsphere_folder = "Templates"
vsphere_dc_name = "CloudLocal"
vsphere_compute_cluster = "vsancluster"
vsphere_host = "esx1.cloud.local"
vsphere_portgroup_name = "DPG-Servers"
vsphere_datastore = "vsanDatastore"
winadmin_password = "secretpassword"
cpu_num = 4
mem_size = 4096
disk_size = 102400
os_iso_path = "[vsanDatastore] ISO/en-us_windows_server_2022_updated_may_2022_x64_dvd_50c4a90e.iso"
vmtools_iso_path = "[vsanDatastore] ISO/windows.iso"
vm_disk_controller_type = ["pvscsi"]
Windows Server 2022 Packer build file
Next, is the actual Packer build file. The Packer build file will pull from the variables.json file. Pay attention below to the floppy files that are defined. Those files are:
- “setup/setup.ps1”
- “setup/vmtools.cmd”
These files are used during the setup of the virtual machine. I will detail their contents in the following section.
- Name win22build.json
#Source block
source "vsphere-iso" "autogenerated_1" {
CPUs = "${var.cpu_num}"
RAM = "${var.mem_size}"
RAM_reserve_all = true
cluster = "${var.vsphere_compute_cluster}"
communicator = "winrm"
convert_to_template = "true"
datacenter = "${var.vsphere_dc_name}"
datastore = "${var.vsphere_datastore}"
disk_controller_type = "${var.vm_disk_controller_type}"
firmware = "efi-secure"
floppy_files = ["setup/win22/efi/autounattend.xml", "setup/setup.ps1", "setup/vmtools.cmd"]
folder = "${var.vsphere_folder}"
guest_os_type = "windows2019srvNext_64Guest"
host = "${var.vsphere_host}"
insecure_connection = "true"
iso_paths = ["${var.os_iso_path}", "${var.vmtools_iso_path}"]
boot_wait = "3s"
boot_command = [
"<spacebar><spacebar>"
]
network_adapters {
network = "${var.vsphere_portgroup_name}"
network_card = "vmxnet3"
}
password = "${var.vsphere_password}"
storage {
disk_size = "${var.disk_size}"
disk_thin_provisioned = true
}
username = "${var.vsphere_user}"
vcenter_server = "${var.vsphere_server}"
vm_name = "${var.vsphere_template_name}"
winrm_password = "${var.winadmin_password}"
winrm_username = "Administrator"
}
#Build block
build {
sources = ["source.vsphere-iso.autogenerated_1"]
provisioner "windows-shell" {
inline = ["dir c:\\"]
}
}
Other Windows Server 2022 Packer Build files
While these files are not required, they can help you have a robust build process with your Windows Server 2022 template. These are the files that I mentioned above. You will notice I am calling these from a setup child directory contained in the directory where I have the Packer variables and build file.
- “setup/setup.ps1” – called in the section <settings pass=”oobeSystem”> of the autounattend.xml file
- “setup/vmtools.cmd” – called in the section <settings pass=”specialize”> of the autounattend.xml file
Notice the contents of those files below. You will see I am copying BGINFO in a not so elegant way as I had a customized BGINFO.ini file I am using. Also, you will see the setup.ps1 file is using the PS Windows Update Module to install all the updates available. I have written about this before and can check that out here:
- Windows Server 2019 Automating Windows Update with PowerShell and Logs
- Automated Windows 10 Installation with AutoUnattend and Packer
- Windows Server 2019 Unattended Install Windows Updates
- setup.ps1
$ErrorActionPreference = "Stop"
# Switch network connection to private mode
# Required for WinRM firewall rules
$profile = Get-NetConnectionProfile
Set-NetConnectionProfile -Name $profile.Name -NetworkCategory Private
# Copy BGINFO and custom INI file
net use x: \\vserv01\packerbuild /persistent:no
xcopy /E X:\BGinfo "c:\program files (x86)\bginfo\"
xcopy X:\startup\bginfo.bat "C:\programdata\microsoft\windows\start menu\programs\startup"
"C:\programdata\microsoft\windows\start menu\programs\startup\bginfo.bat"
#Install PS Windows Update Module
Get-PackageProvider -name nuget -force
Install-Module PSWindowsUpdate -confirm:$false -force
Get-WindowsUpdate -MicrosoftUpdate -install -IgnoreUserInput -acceptall -AutoReboot | Out-File -filepath 'c:\windowsupdate.log' -append
#WinRM Configure
winrm quickconfig -quiet
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
# Reset auto logon count
# https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-shell-setup-autologon-logoncount#logoncount-known-issue
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name AutoLogonCount -Value 0
As for the VMware Tools cmd file, this is what kicks off the installation of VMware Tools during the “specialize” stage of the answer file.
- vmtools.cmd
@rem Silent mode, basic UI, no reboot
rem e:\setup64 /s /v "/qb REBOOT=R"
e:\setup64 /s /v "/qn reboot=ReallySuppress"
Running the Automated Windows Server 2022 Packer Build
Once you have the files created and in place (in the directories you want, etc.), all that is left to do is run the Packer build workflow. Running init and validate first ensures you have everything you need to run the packer build, including plugins, etc. and that your syntax is correct.
packer init .
packer validate .
packer build .
The build will proceed, and you will be able to see from the virtual machine console, if you have a connection open, the server will build, updates will be applied, and all the other configurations specified before powering down the Windows Server 2022 VM and converting it to a template.
Video overview of Windows Server template automation with Packer
Download the files from my GitHub Packer Repo
Hopefully, this walkthrough will show how easy it is using a few files to get up and running with a clean build of the brand new Windows Server 2022 Preview build which is available for download.
Thanks for the guide! If I don’t have a DC, is that an issue? Can I just leave the dc_name blank?
Is this assuming that the ISO is already downloaded and hosted in your datastore? I believe that is the case but could we not make it the way you have for Ubuntu, or have you done this based on the variation on ISO downloads available for Windows?
Michael, thanks so much for the comment! Yes that is the case. you could definitely pull this down using the same method as the Ubuntu build and honestly has been on my list of things to do in the lab. This was a quick and dirty way to reference the ISO initially when building my images for the lab.
Hi Brandon,
I am a newbie to packer and windows automation; does this article instruction also work with automating windows server 2019 standard desktop version as well? I tried using your instructions in your older article – https://www.virtualizationhowto.com/2019/10/automate-windows-server-2019-core-installation-and-deployment but it seems like new version of packer uses looks for hcl extensions instead .json. I could be wrong, but like I said, I’m still a newbie.
Hey Henry. Thank you for the comment! Actually, there is a cool little parameter in Packer called the “hcl2_upgrade” parameter. You can feed it the json files and it will convert them for you. I think I have converted the windows 2019 build on my side though. Reach out in the forum if you would like some help on that front, maybe under the “DevOps” forum area or “VMware vSphere help” forum.
Thanks again,
Brandon
thank you for the guidance.
Hi Brandon,
I am a little confused on your automated windows packer build. Once this build is completed, do I convert this in vcenter to template to deploy my windows server vms or is the packer process used to deploy windows server vms?
Hey Henry,
Thank you for the comment. I will take up the questions on the forum as I see you have opened a topic there. Awesome.
Brandon