Search code examples
windowsamazon-ec2packerwinrm

How do I set up WinRM for Windows 2025 Server with Packer? Waiting for WinRM to become available


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

Solution

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