Tom McLean
Tom McLean

Reputation: 6369

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

Upvotes: 0

Views: 203

Answers (1)

Tom McLean
Tom McLean

Reputation: 6369

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
}

Upvotes: 0

Related Questions