I am trying to create an AMI of a windows server using packer following this repo, however it seems like its using an older version of windows server which doesnt seem to work. How can I create a minimal packer example for configuring a Windows 2025 Server AMI?
My starting packer file is:
packer {
required_plugins {
amazon = {
version = ">= 1.3.2"
source = "github.com/hashicorp/amazon"
}
}
}
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
ami_name = var.ami_name != "" ? var.ami_name : "packer-${var.image_os}-${var.image_version}"
}
variable "allowed_inbound_ip_addresses" {
type = list(string)
default = []
}
variable "aws_tags" {
type = map(string)
default = {}
}
variable "region" {
type = string
default = "eu-west-2"
}
variable "image_os" {
type = string
default = "ubuntu"
}
variable "image_version" {
type = string
default = "dev"
}
variable "ami_name" {
type = string
default = ""
}
variable "ami_region" {
type = string
default = "eu-west-2"
}
variable "instance_type" {
type = string
default = "t2.medium"
}
variable "subnet_id" {
type = string
default = "subnet-someid"
}
source "amazon-ebs" "windows_image" {
ami_name = "ado-windows-${local.timestamp}"
instance_type = "t3.medium"
region = "${var.region}"
source_ami_filter {
filters = {
name = "Windows_Server-2025*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["amazon"]
}
user_data_file = "${path.root}/../scripts/build/bootstrap_win.txt"
communicator = "winrm"
winrm_username = "Administrator"
winrm_password = "SuperS3cr3t!!!"
winrm_insecure = true
winrm_use_ssl = true
subnet_id = "${var.subnet_id}"
}
build {
name = "ado-windows-build"
sources = ["source.amazon-ebs.windows_image"]
provisioner "powershell" {
environment_vars = ["DEVOPS_LIFE_IMPROVER=PACKER"]
inline = ["Write-Host \"HELLO NEW USER; WELCOME TO $Env:DEVOPS_LIFE_IMPROVER\"", "Write-Host \"You need to use backtick escapes when using\"", "Write-Host \"characters such as DOLLAR`$ directly in a command\"", "Write-Host \"or in your own scripts.\""]
}
}
and bootstrap_win.txt
# A Packer config that works with this example would be:
#
#
# "winrm_username": "Administrator",
# "winrm_password": "SuperS3cr3t!!!",
# "winrm_insecure": true,
# "winrm_use_ssl": true
#
#
<powershell>
# Create username and password
net user Administrator SuperS3cr3t!!!
wmic useraccount where "name='Administrator'" set PasswordExpires=FALSE
Set-ExecutionPolicy Unrestricted -Scope LocalMachine -Force -ErrorAction Ignore
# Don't set this before Set-ExecutionPolicy as it throws an error
$ErrorActionPreference = "stop"
# Remove HTTP listener
Remove-Item -Path WSMan:\Localhost\listener\listener* -Recurse
# Create a self-signed certificate to let ssl work
$Cert = New-SelfSignedCertificate -CertstoreLocation Cert:\LocalMachine\My -DnsName "packer"
New-Item -Path WSMan:\LocalHost\Listener -Transport HTTPS -Address * -CertificateThumbPrint $Cert.Thumbprint -Force
# WinRM
write-output "Setting up WinRM"
write-host "(host) setting up WinRM"
# Configure WinRM to allow unencrypted communication, and provide the
# self-signed cert to the WinRM listener.
cmd.exe /c winrm quickconfig -q
cmd.exe /c winrm set "winrm/config/service" '@{AllowUnencrypted="true"}'
cmd.exe /c winrm set "winrm/config/client" '@{AllowUnencrypted="true"}'
cmd.exe /c winrm set "winrm/config/service/auth" '@{Basic="true"}'
cmd.exe /c winrm set "winrm/config/client/auth" '@{Basic="true"}'
cmd.exe /c winrm set "winrm/config/service/auth" '@{CredSSP="true"}'
cmd.exe /c winrm set "winrm/config/listener?Address=*+Transport=HTTPS" "@{Port=`"5986`";Hostname=`"packer`";CertificateThumbprint=`"$($Cert.Thumbprint)`"}"
# Make sure appropriate firewall port openings exist
cmd.exe /c netsh advfirewall firewall set rule group="remote administration" new enable=yes
cmd.exe /c netsh firewall add portopening TCP 5986 "Port 5986"
# Restart WinRM, and set it so that it auto-launches on startup.
cmd.exe /c net stop winrm
cmd.exe /c sc config winrm start= auto
cmd.exe /c net start winrm
</powershell>
However it is getting stuck here:
==> ado-windows-build.amazon-ebs.windows_image: Waiting for WinRM to become available...
If you are using packer, you can use this script to set up WinRM:
<powershell>
# Set administrator password
net user ${winrm_username} ${winrm_password}
wmic useraccount where "name='${winrm_username}'" set PasswordExpires=FALSE
# First, make sure WinRM can't be connected to
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new enable=yes action=block
# Delete any existing WinRM listeners
winrm delete winrm/config/listener?Address=*+Transport=HTTP 2>$Null
winrm delete winrm/config/listener?Address=*+Transport=HTTPS 2>$Null
# Disable group policies which block basic authentication and unencrypted login
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowBasic -Value 1
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Client -Name AllowUnencryptedTraffic -Value 1
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowBasic -Value 1
Set-ItemProperty -Path HKLM:\Software\Policies\Microsoft\Windows\WinRM\Service -Name AllowUnencryptedTraffic -Value 1
# Create a new WinRM listener and configure
winrm create winrm/config/listener?Address=*+Transport=HTTP
winrm set winrm/config/winrs '@{MaxMemoryPerShellMB="0"}'
winrm set winrm/config '@{MaxTimeoutms="7200000"}'
winrm set winrm/config/service '@{AllowUnencrypted="true"}'
winrm set winrm/config/service '@{MaxConcurrentOperationsPerUser="12000"}'
winrm set winrm/config/service/auth '@{Basic="true"}'
winrm set winrm/config/client/auth '@{Basic="true"}'
# Configure UAC to allow privilege elevation in remote shells
$Key = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System'
$Setting = 'LocalAccountTokenFilterPolicy'
Set-ItemProperty -Path $Key -Name $Setting -Value 1 -Force
# Configure and restart the WinRM Service; Enable the required firewall exception
Stop-Service -Name WinRM
Set-Service -Name WinRM -StartupType Automatic
netsh advfirewall firewall set rule name="Windows Remote Management (HTTP-In)" new action=allow localip=any remoteip=any
Start-Service -Name WinRM
</powershell>
Which can be set up as user_data like so:
variable "winrm_username" {
type = string
default = "Administrator"
}
variable "winrm_password" {
type = string
default = "MySuperSecurePassword"
}
locals {
timestamp = regex_replace(timestamp(), "[- TZ:]", "")
name = "windows-222-full-ado-build-agent-${local.timestamp}"
winrm_password = var.winrm_password
winrm_username = var.winrm_username
}
source "amazon-ebs" "this" {
skip_create_ami = var.skip_ami
ami_name = local.name
communicator = "winrm"
instance_type = "t3a.large"
region = "${var.region}"
# iam_instance_profile = "AmazonSSMRoleForInstancesQuickSetup"
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
run_tags = {
Name = "packer-builder-${local.name}"
}
tags = {
Name = local.name
}
ami_block_device_mappings {
device_name = "/dev/sda1"
volume_size = 32
}
source_ami_filter {
filters = {
name = "Windows_Server-2022-English-Full-Base*"
root-device-type = "ebs"
virtualization-type = "hvm"
}
most_recent = true
owners = ["amazon"]
}
subnet_id = "${var.subnet_id}"
user_data = templatefile("${path.root}/../scripts/build/bootstrap_win.pkrtpl.hcl", { winrm_username = local.winrm_username, winrm_password = local.winrm_password })
winrm_username = local.winrm_username
winrm_password = local.winrm_password
}