Windows Server 2022

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:

  • 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.

Automated windows server 2022 packer build
Automated windows server 2022 packer build

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.

Subscribe to VirtualizationHowto via Email 🔔

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Brandon Lee

Brandon Lee is the Senior Writer, Engineer and owner at Virtualizationhowto.com and has over two decades of experience in Information Technology. Having worked for numerous Fortune 500 companies as well as in various industries, Brandon has extensive experience in various IT segments and is a strong advocate for open source technologies. Brandon holds many industry certifications, loves the outdoors and spending time with family.

Related Articles

3 Comments

  1. 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?

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.