Introduction

HashiCorp Packer is an open-source tool that automates the creation of identical machine images for multiple platforms from a single configuration file. Building custom Windows Server 2016 images with Packer ensures that every virtual machine deployed in your environment starts from a hardened, pre-configured baseline — with all Windows updates, security settings, required software, and monitoring agents pre-installed. This eliminates configuration drift and dramatically speeds up VM provisioning in Hyper-V, VMware, Azure, AWS, and other platforms.

Installing Packer on the Build Host

Install Packer on a Linux build server (or Windows) that has access to your hypervisor:

# On Linux build host (Ubuntu/Debian)
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/hashicorp.list
apt-get update && apt-get install -y packer

# Verify installation
packer version

# Install Packer plugins for Hyper-V and Azure
packer plugins install github.com/hashicorp/hyperv
packer plugins install github.com/hashicorp/azure

Creating the Packer HCL Template for Hyper-V

Write a Packer HCL2 configuration file to build a Windows Server 2016 image on Hyper-V:

cat > /packer/ws2016-hyperv.pkr.hcl << 'EOF'
variable "iso_url" {
  default = "file:///isos/en_windows_server_2016_x64.iso"
}
variable "iso_checksum" {
  default = "sha256:1ce702a578a3cb1ac3d14873980838590f06d5b7101c5daaccbac9d73f1fb50f"
}

source "hyperv-iso" "ws2016" {
  iso_url          = var.iso_url
  iso_checksum     = var.iso_checksum
  vm_name          = "WS2016-BaseImage"
  cpus             = 4
  memory           = 4096
  disk_size        = 61440
  switch_name      = "External Switch"
  generation       = 2
  secure_boot      = true
  guest_additions_mode = "disable"
  headless         = true
  communicator     = "winrm"
  winrm_username   = "Administrator"
  winrm_password   = "Temp@dmin2016!"
  winrm_use_ssl    = false
  winrm_timeout    = "60m"
  floppy_files     = ["./answer_files/Autounattend.xml"]
  boot_wait        = "60s"
  shutdown_command = "shutdown /s /t 10 /f /d p:4:1 /c 'Packer Shutdown'"
}

build {
  sources = ["source.hyperv-iso.ws2016"]

  provisioner "powershell" {
    scripts = [
      "scripts/01-initial-setup.ps1",
      "scripts/02-windows-updates.ps1",
      "scripts/03-install-agents.ps1",
      "scripts/04-security-hardening.ps1",
      "scripts/05-sysprep.ps1"
    ]
  }

  post-processor "manifest" {
    output = "manifest.json"
    strip_path = true
  }
}
EOF

Writing the Autounattend.xml Answer File

Create the Windows Setup answer file to automate the OS installation:

mkdir -p /packer/answer_files
cat > /packer/answer_files/Autounattend.xml << 'EOF'


  
    
      en-US
      en-US
      en-US
      en-US
    
    
      
        
          
            
              EFI1100
            
            
              MSR216
            
            
              Primary3true
            
          
          0true
        
      
      
        
          03
        
      
      
        true
        Never
      
    
  

EOF

Writing Provisioner Scripts

Create PowerShell scripts that Packer runs inside the VM after installation:

mkdir -p /packer/scripts

# 01-initial-setup.ps1 — Disable unnecessary services, configure WinRM
cat > /packer/scripts/01-initial-setup.ps1 < /packer/scripts/04-security-hardening.ps1 << 'EOF'
# Require NTLMv2
Set-ItemProperty 'HKLM:SYSTEMCurrentControlSetControlLsa' -Name LmCompatibilityLevel -Value 5

# Enable Windows Firewall on all profiles
Set-NetFirewallProfile -Profile Domain,Private,Public -Enabled True

# Enable audit logging
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Account Logon" /success:enable /failure:enable

# Disable SMBv1
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force

# Set minimum TLS to 1.2
$tls12 = 'HKLM:SYSTEMCurrentControlSetControlSecurityProvidersSCHANNELProtocolsTLS 1.2Server'
New-Item -Path $tls12 -Force | Out-Null
Set-ItemProperty -Path $tls12 -Name Enabled -Value 1
Set-ItemProperty -Path $tls12 -Name DisabledByDefault -Value 0
EOF

Building the Image

Run Packer to build the Windows Server 2016 image:

cd /packer

# Validate the template
packer validate ws2016-hyperv.pkr.hcl

# Build the image (verbose output)
PACKER_LOG=1 packer build ws2016-hyperv.pkr.hcl 2>&1 | tee /var/log/packer-ws2016.log

# Check the output manifest
cat manifest.json | python3 -m json.tool

Summary

Packer automates the creation of standardised, security-hardened Windows Server 2016 base images that eliminate configuration drift across your infrastructure. With an Autounattend.xml answer file, layered PowerShell provisioner scripts, and a declarative HCL template, you can produce identical gold images for Hyper-V, VMware, Azure, or AWS in a fully automated pipeline. These base images form the foundation for immutable infrastructure and significantly accelerate VM deployment times in any environment.