Introduction to Packer for Windows Server Image Building
HashiCorp Packer is a tool that automates the creation of machine images from a single configuration file. For Windows Server 2019, Packer can build Hyper-V VHDs, VMware VMDK templates, AWS AMIs, Azure managed images, or VirtualBox OVAs—all from the same template definition. Custom Windows Server 2019 images pre-baked with security hardening, required software, and configuration eliminate configuration drift and reduce deployment time from hours to minutes.
Prerequisites
# Install Packer on a Windows Server 2019 build machine
# Download from https://www.packer.io/downloads
# Or use Chocolatey:
choco install packer -y
# Verify installation
packer version
packer plugins installed
# Install required Packer plugins
packer plugins install github.com/hashicorp/vsphere
packer plugins install github.com/hashicorp/hyperv
packer plugins install github.com/hashicorp/azure
packer plugins install github.com/hashicorp/windows-update
Building a Windows Server 2019 Image for Hyper-V
The Packer HCL template defines sources (build environments) and build steps. Below is a complete template for a hardened Windows Server 2019 Server Core image on Hyper-V:
# windows-server-2019.pkr.hcl
packer {
required_plugins {
hyperv = {
version = ">= 1.1.0"
source = "github.com/hashicorp/hyperv"
}
windows-update = {
version = ">= 0.15.0"
source = "github.com/rgl/windows-update"
}
}
}
variable "iso_path" {
type = string
default = "D:\ISOs\WS2019_SERVERDATACENTER.iso"
}
variable "iso_checksum" {
type = string
default = "sha256:the_sha256_checksum_of_your_iso"
}
variable "output_dir" {
type = string
default = "D:\Packer\output\ws2019-core"
}
source "hyperv-iso" "ws2019-core" {
iso_url = var.iso_path
iso_checksum = var.iso_checksum
vm_name = "ws2019-core-template"
generation = 2
cpus = 4
ram_size = 4096
disk_size = 61440
switch_name = "vSwitch-Build"
enable_secure_boot = true
secure_boot_template = "MicrosoftWindows"
headless = false
shutdown_command = "shutdown /s /t 10 /f /d p:4:1 /c 'Packer Shutdown'"
shutdown_timeout = "30m"
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "Packer!Build2024"
winrm_use_ssl = false
winrm_insecure = true
winrm_timeout = "2h"
# Unattend.xml passed as a virtual floppy for automated installation
floppy_files = ["./autounattend.xml", "./SetupComplete.ps1"]
}
build {
sources = ["source.hyperv-iso.ws2019-core"]
# Install Windows Updates
provisioner "windows-update" {
search_criteria = "IsInstalled=0"
filters = [
"include:$_.Title -match 'Security Update'",
"exclude:$_.Title -match 'Preview'",
]
update_limit = 50
restart_timeout = "2h"
}
# Run hardening script
provisioner "powershell" {
script = "./scripts/Harden-Server.ps1"
}
# Run DSC configuration
provisioner "powershell" {
inline = [
"Install-Module PSDesiredStateConfiguration -Force",
"& C:\Windows\Temp\Apply-DSCBaseline.ps1"
]
}
# Sysprep for template generalization
provisioner "windows-restart" { }
provisioner "powershell" {
inline = [
"C:\Windows\System32\Sysprep\sysprep.exe /oobe /generalize /quiet /shutdown /unattend:C:\Windows\Temp\sysprep.xml"
]
}
post-processor "manifest" {
output = "${var.output_dir}/packer-manifest.json"
}
}
Autounattend.xml for Automated Installation
# autounattend.xml (Server Core, Datacenter Edition)
# Place this file on the floppy or at the root of a virtual DVD
en-US
en-US
en-US
en-US
en-US
1EFI100
2MSR16
3Primarytrue
0true
/IMAGE/INDEX2
03
true
Never
true
true
true
true
true
3
Packer!Build2024true
UTC
Server Hardening Script Invoked by Packer
# scripts/Harden-Server.ps1
$ErrorActionPreference = 'Stop'
# Set TLS 1.2 as default
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
# Disable SMBv1
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
# Disable NetBIOS
Get-WmiObject -Class Win32_NetworkAdapterConfiguration |
Where-Object { $_.IPEnabled } |
ForEach-Object { $_.SetTcpipNetbios(2) }
# Enable Windows Firewall on all profiles
Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled True
# Disable Remote Registry
Set-Service -Name RemoteRegistry -StartupType Disabled
Stop-Service -Name RemoteRegistry -Force
# Enable Windows Defender
Set-MpPreference -DisableRealtimeMonitoring $false
Update-MpSignature
# Enable Windows Update automatic download
$wuKey = 'HKLM:SOFTWAREPoliciesMicrosoftWindowsWindowsUpdateAU'
New-Item -Path $wuKey -Force | Out-Null
Set-ItemProperty $wuKey AUOptions 3 # 3 = download only, notify to install
# Configure NTP
w32tm /config /manualpeerlist:"time.windows.com" /syncfromflags:manual /reliable:YES /update
Restart-Service w32tm
# Set screen saver / idle lock
Set-ItemProperty 'HKCU:Control PanelDesktop' -Name 'ScreenSaveTimeOut' -Value 300
Set-ItemProperty 'HKCU:Control PanelDesktop' -Name 'ScreenSaverIsSecure' -Value 1
Write-Output 'Hardening complete'
Building the Image
# Validate the template syntax
packer validate windows-server-2019.pkr.hcl
# Run the build (verbose output)
packer build -on-error=ask windows-server-2019.pkr.hcl
# Build with variable overrides
packer build `
-var "iso_path=E:ISOsWS2019_Updated.iso" `
-var "output_dir=D:Imagesws2019-q1" `
windows-server-2019.pkr.hcl
# Build only a specific source
packer build -only=hyperv-iso.ws2019-core windows-server-2019.pkr.hcl
# After successful build, the VHD is at:
# D:Packeroutputws2019-coreVirtual Hard Disks*.vhdx
Building for Azure
# Add an Azure source to the same template
source "azure-arm" "ws2019-azure" {
subscription_id = var.azure_subscription_id
client_id = var.azure_client_id
client_secret = var.azure_client_secret
tenant_id = var.azure_tenant_id
managed_image_name = "ws2019-core-hardened"
managed_image_resource_group_name = "rg-packer-images"
location = "East US"
os_type = "Windows"
image_publisher = "MicrosoftWindowsServer"
image_offer = "WindowsServer"
image_sku = "2019-datacenter-core"
image_version = "latest"
vm_size = "Standard_D2s_v3"
disk_caching_type = "ReadWrite"
communicator = "winrm"
winrm_use_ssl = true
winrm_insecure = true
winrm_timeout = "10m"
winrm_username = "packer"
}
# Build for Azure
packer build -only=azure-arm.ws2019-azure windows-server-2019.pkr.hcl
Conclusion
Packer automates the creation of Windows Server 2019 images that are pre-hardened, fully patched, and consistently configured before a single VM is ever deployed from them. Using the HCL2 template format, a single configuration file can build images for Hyper-V, VMware, Azure, and AWS simultaneously. Combined with a PowerShell hardening script, Windows Update provisioner, and DSC baseline, Packer images ensure that new servers are born compliant rather than having compliance applied reactively after deployment.