From 24a29873e497c94bd24325c021d302f9c8c7045a Mon Sep 17 00:00:00 2001 From: Jon Anderson <1361123+jonanderson10@users.noreply.github.com> Date: Fri, 14 Nov 2025 13:55:56 -0800 Subject: [PATCH 1/6] feat: add ubuntu 24.04 (noble) image examples --- .../github_agent.ubuntu.pkr.hcl | 215 ++++++++++++++++++ .../ubuntu-noble/github_agent.ubuntu.pkr.hcl | 215 ++++++++++++++++++ 2 files changed, 430 insertions(+) create mode 100644 images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl create mode 100644 images/ubuntu-noble/github_agent.ubuntu.pkr.hcl diff --git a/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl b/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl new file mode 100644 index 0000000000..fd051ae0d6 --- /dev/null +++ b/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl @@ -0,0 +1,215 @@ +packer { + required_plugins { + amazon = { + version = ">= 0.0.2" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "runner_version" { + description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." + default = null +} + +variable "region" { + description = "The region to build the image in" + type = string + default = "eu-west-1" +} + +variable "security_group_id" { + description = "The ID of the security group Packer will associate with the builder to enable access" + type = string + default = null +} + +variable "subnet_id" { + description = "If using VPC, the ID of the subnet, such as subnet-12345def, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC" + type = string + default = null +} + +variable "associate_public_ip_address" { + description = "If using a non-default VPC, there is no public IP address assigned to the EC2 instance. If you specified a public subnet, you probably want to set this to true. Otherwise the EC2 instance won't have access to the internet" + type = string + default = null +} + +variable "instance_type" { + description = "The instance type Packer will use for the builder" + type = string + default = "t4g.small" +} + +variable "iam_instance_profile" { + description = "IAM instance profile Packer will use for the builder. An empty string (default) means no profile will be assigned." + type = string + default = "" +} + +variable "root_volume_size_gb" { + type = number + default = 8 +} + +variable "ebs_delete_on_termination" { + description = "Indicates whether the EBS volume is deleted on instance termination." + type = bool + default = true +} + +variable "global_tags" { + description = "Tags to apply to everything" + type = map(string) + default = {} +} + +variable "ami_tags" { + description = "Tags to apply to the AMI" + type = map(string) + default = {} +} + +variable "snapshot_tags" { + description = "Tags to apply to the snapshot" + type = map(string) + default = {} +} + +variable "custom_shell_commands" { + description = "Additional commands to run on the EC2 instance, to customize the instance, like installing packages" + type = list(string) + default = [] +} + +variable "temporary_security_group_source_public_ip" { + description = "When enabled, use public IP of the host (obtained from https://checkip.amazonaws.com) as CIDR block to be authorized access to the instance, when packer is creating a temporary security group. Note: If you specify `security_group_id` then this input is ignored." + type = bool + default = false +} + +data "http" github_runner_release_json { + url = "https://api.github.com/repos/actions/runner/releases/latest" + request_headers = { + Accept = "application/vnd.github+json" + X-GitHub-Api-Version : "2022-11-28" + } +} + +locals { + runner_version = coalesce(var.runner_version, trimprefix(jsondecode(data.http.github_runner_release_json.body).tag_name, "v")) +} + +source "amazon-ebs" "githubrunner" { + ami_name = "github-runner-ubuntu-noble-arm64-${formatdate("YYYYMMDDhhmm", timestamp())}" + instance_type = var.instance_type + iam_instance_profile = var.iam_instance_profile + region = var.region + security_group_id = var.security_group_id + subnet_id = var.subnet_id + associate_public_ip_address = var.associate_public_ip_address + temporary_security_group_source_public_ip = var.temporary_security_group_source_public_ip + + source_ami_filter { + filters = { + name = "*ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-arm64-server-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["099720109477"] + } + ssh_username = "ubuntu" + tags = merge( + var.global_tags, + var.ami_tags, + { + OS_Version = "ubuntu-noble" + Release = "Latest" + Base_AMI_Name = "{{ .SourceAMIName }}" + }) + snapshot_tags = merge( + var.global_tags, + var.snapshot_tags, + ) + + launch_block_device_mappings { + device_name = "/dev/sda1" + volume_size = "${var.root_volume_size_gb}" + volume_type = "gp3" + delete_on_termination = "${var.ebs_delete_on_termination}" + } +} + +build { + name = "githubactions-runner" + sources = [ + "source.amazon-ebs.githubrunner" + ] + provisioner "shell" { + environment_vars = [ + "DEBIAN_FRONTEND=noninteractive" + ] + inline = concat([ + "sudo cloud-init status --wait", + "sudo apt-get update", + "sudo apt-get -y install ca-certificates curl gnupg lsb-release", + "sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg", + "echo deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null", + "sudo apt-get -y update", + "sudo apt-get -y install docker-ce docker-ce-cli containerd.io jq git unzip build-essential", + "sudo systemctl enable containerd.service", + "sudo service docker start", + "sudo usermod -a -G docker ubuntu", + "sudo curl -f https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/arm64/latest/amazon-cloudwatch-agent.deb -o amazon-cloudwatch-agent.deb", + "sudo dpkg -i amazon-cloudwatch-agent.deb", + "sudo systemctl restart amazon-cloudwatch-agent", + "sudo curl -f https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o awscliv2.zip", + "unzip awscliv2.zip", + "sudo ./aws/install", + ], var.custom_shell_commands) + } + + provisioner "file" { + content = templatefile("../install-runner.sh", { + install_runner = templatefile("../../modules/runners/templates/install-runner.sh", { + ARM_PATCH = "" + S3_LOCATION_RUNNER_DISTRIBUTION = "" + RUNNER_ARCHITECTURE = "arm64" + }) + }) + destination = "/tmp/install-runner.sh" + } + + provisioner "shell" { + environment_vars = [ + "RUNNER_TARBALL_URL=https://github.com/actions/runner/releases/download/v${local.runner_version}/actions-runner-linux-arm64-${local.runner_version}.tar.gz" + ] + inline = [ + "sudo chmod +x /tmp/install-runner.sh", + "echo ubuntu | tee -a /tmp/install-user.txt", + "sudo RUNNER_ARCHITECTURE=arm64 RUNNER_TARBALL_URL=$RUNNER_TARBALL_URL /tmp/install-runner.sh", + "echo ImageOS=ubuntu24 | tee -a /opt/actions-runner/.env" + ] + } + + provisioner "file" { + content = templatefile("../start-runner.sh", { + start_runner = templatefile("../../modules/runners/templates/start-runner.sh", { metadata_tags = "enabled" }) + }) + destination = "/tmp/start-runner.sh" + } + + provisioner "shell" { + inline = [ + "sudo mv /tmp/start-runner.sh /var/lib/cloud/scripts/per-boot/start-runner.sh", + "sudo chmod +x /var/lib/cloud/scripts/per-boot/start-runner.sh", + ] + } + + post-processor "manifest" { + output = "manifest.json" + strip_path = true + } +} diff --git a/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl b/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl new file mode 100644 index 0000000000..b0f42277f0 --- /dev/null +++ b/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl @@ -0,0 +1,215 @@ +packer { + required_plugins { + amazon = { + version = ">= 0.0.2" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "runner_version" { + description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." + default = null +} + +variable "region" { + description = "The region to build the image in" + type = string + default = "eu-west-1" +} + +variable "security_group_id" { + description = "The ID of the security group Packer will associate with the builder to enable access" + type = string + default = null +} + +variable "subnet_id" { + description = "If using VPC, the ID of the subnet, such as subnet-12345def, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC" + type = string + default = null +} + +variable "associate_public_ip_address" { + description = "If using a non-default VPC, there is no public IP address assigned to the EC2 instance. If you specified a public subnet, you probably want to set this to true. Otherwise the EC2 instance won't have access to the internet" + type = string + default = null +} + +variable "instance_type" { + description = "The instance type Packer will use for the builder" + type = string + default = "t3.medium" +} + +variable "iam_instance_profile" { + description = "IAM instance profile Packer will use for the builder. An empty string (default) means no profile will be assigned." + type = string + default = "" +} + +variable "root_volume_size_gb" { + type = number + default = 8 +} + +variable "ebs_delete_on_termination" { + description = "Indicates whether the EBS volume is deleted on instance termination." + type = bool + default = true +} + +variable "global_tags" { + description = "Tags to apply to everything" + type = map(string) + default = {} +} + +variable "ami_tags" { + description = "Tags to apply to the AMI" + type = map(string) + default = {} +} + +variable "snapshot_tags" { + description = "Tags to apply to the snapshot" + type = map(string) + default = {} +} + +variable "custom_shell_commands" { + description = "Additional commands to run on the EC2 instance, to customize the instance, like installing packages" + type = list(string) + default = [] +} + +variable "temporary_security_group_source_public_ip" { + description = "When enabled, use public IP of the host (obtained from https://checkip.amazonaws.com) as CIDR block to be authorized access to the instance, when packer is creating a temporary security group. Note: If you specify `security_group_id` then this input is ignored." + type = bool + default = false +} + +data "http" github_runner_release_json { + url = "https://api.github.com/repos/actions/runner/releases/latest" + request_headers = { + Accept = "application/vnd.github+json" + X-GitHub-Api-Version : "2022-11-28" + } +} + +locals { + runner_version = coalesce(var.runner_version, trimprefix(jsondecode(data.http.github_runner_release_json.body).tag_name, "v")) +} + +source "amazon-ebs" "githubrunner" { + ami_name = "github-runner-ubuntu-noble-amd64-${formatdate("YYYYMMDDhhmm", timestamp())}" + instance_type = var.instance_type + iam_instance_profile = var.iam_instance_profile + region = var.region + security_group_id = var.security_group_id + subnet_id = var.subnet_id + associate_public_ip_address = var.associate_public_ip_address + temporary_security_group_source_public_ip = var.temporary_security_group_source_public_ip + + source_ami_filter { + filters = { + name = "*ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["099720109477"] + } + ssh_username = "ubuntu" + tags = merge( + var.global_tags, + var.ami_tags, + { + OS_Version = "ubuntu-noble" + Release = "Latest" + Base_AMI_Name = "{{ .SourceAMIName }}" + }) + snapshot_tags = merge( + var.global_tags, + var.snapshot_tags, + ) + + launch_block_device_mappings { + device_name = "/dev/sda1" + volume_size = "${var.root_volume_size_gb}" + volume_type = "gp3" + delete_on_termination = "${var.ebs_delete_on_termination}" + } +} + +build { + name = "githubactions-runner" + sources = [ + "source.amazon-ebs.githubrunner" + ] + provisioner "shell" { + environment_vars = [ + "DEBIAN_FRONTEND=noninteractive" + ] + inline = concat([ + "sudo cloud-init status --wait", + "sudo apt-get -y update", + "sudo apt-get -y install ca-certificates curl gnupg lsb-release", + "sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg", + "echo deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null", + "sudo apt-get -y update", + "sudo apt-get -y install docker-ce docker-ce-cli containerd.io jq git unzip", + "sudo systemctl enable containerd.service", + "sudo service docker start", + "sudo usermod -a -G docker ubuntu", + "sudo curl -f https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb -o amazon-cloudwatch-agent.deb", + "sudo dpkg -i amazon-cloudwatch-agent.deb", + "sudo systemctl restart amazon-cloudwatch-agent", + "sudo curl -f https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip", + "unzip awscliv2.zip", + "sudo ./aws/install", + ], var.custom_shell_commands) + } + + provisioner "file" { + content = templatefile("../install-runner.sh", { + install_runner = templatefile("../../modules/runners/templates/install-runner.sh", { + ARM_PATCH = "" + S3_LOCATION_RUNNER_DISTRIBUTION = "" + RUNNER_ARCHITECTURE = "x64" + }) + }) + destination = "/tmp/install-runner.sh" + } + + provisioner "shell" { + environment_vars = [ + "RUNNER_TARBALL_URL=https://github.com/actions/runner/releases/download/v${local.runner_version}/actions-runner-linux-x64-${local.runner_version}.tar.gz" + ] + inline = [ + "sudo chmod +x /tmp/install-runner.sh", + "echo ubuntu | tee -a /tmp/install-user.txt", + "sudo RUNNER_ARCHITECTURE=x64 RUNNER_TARBALL_URL=$RUNNER_TARBALL_URL /tmp/install-runner.sh", + "echo ImageOS=ubuntu24 | tee -a /opt/actions-runner/.env" + ] + } + + provisioner "file" { + content = templatefile("../start-runner.sh", { + start_runner = templatefile("../../modules/runners/templates/start-runner.sh", { metadata_tags = "enabled" }) + }) + destination = "/tmp/start-runner.sh" + } + + provisioner "shell" { + inline = [ + "sudo mv /tmp/start-runner.sh /var/lib/cloud/scripts/per-boot/start-runner.sh", + "sudo chmod +x /var/lib/cloud/scripts/per-boot/start-runner.sh", + ] + } + + post-processor "manifest" { + output = "manifest.json" + strip_path = true + } +} From da3365c3b9b970fccf8e8623fd9701fcff1e426e Mon Sep 17 00:00:00 2001 From: Jon Anderson <1361123+jonanderson10@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:25:57 -0800 Subject: [PATCH 2/6] feat: windows 2025 image example fix: windows 2025 uses ec2launchv2 --- images/windows-core-2025/bootstrap_win.ps1 | 38 +++++ .../github_agent.windows.pkr.hcl | 148 ++++++++++++++++++ .../windows-core-2025/windows-provisioner.ps1 | 53 +++++++ 3 files changed, 239 insertions(+) create mode 100644 images/windows-core-2025/bootstrap_win.ps1 create mode 100644 images/windows-core-2025/github_agent.windows.pkr.hcl create mode 100644 images/windows-core-2025/windows-provisioner.ps1 diff --git a/images/windows-core-2025/bootstrap_win.ps1 b/images/windows-core-2025/bootstrap_win.ps1 new file mode 100644 index 0000000000..3cba59a089 --- /dev/null +++ b/images/windows-core-2025/bootstrap_win.ps1 @@ -0,0 +1,38 @@ + + +Write-Output "Running User Data Script" +Write-Host "(host) Running User Data Script" + +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" + +# I'm not really sure why we need the cmd.exe wrapper, but it works with it and doesn't work without it +cmd.exe /c winrm quickconfig -q +cmd.exe /c winrm set "winrm/config" '@{MaxTimeoutms="1800000"}' +cmd.exe /c winrm set "winrm/config/winrs" '@{MaxMemoryPerShellMB="1024"}' +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)`"}" +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" +cmd.exe /c net stop winrm +cmd.exe /c sc config winrm start= auto +cmd.exe /c net start winrm + + \ No newline at end of file diff --git a/images/windows-core-2025/github_agent.windows.pkr.hcl b/images/windows-core-2025/github_agent.windows.pkr.hcl new file mode 100644 index 0000000000..5729575ac5 --- /dev/null +++ b/images/windows-core-2025/github_agent.windows.pkr.hcl @@ -0,0 +1,148 @@ +packer { + required_plugins { + amazon = { + version = ">= 0.0.2" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "runner_version" { + description = "The version (no v prefix) of the runner software to install https://github.com/actions/runner/releases. The latest release will be fetched from GitHub if not provided." + default = null +} + +variable "region" { + description = "The region to build the image in" + type = string + default = "eu-west-1" +} + +variable "instance_type" { + description = "The instance type Packer will use for the builder" + type = string + default = "m4.xlarge" +} + +variable "iam_instance_profile" { + description = "IAM instance profile Packer will use for the builder. An empty string (default) means no profile will be assigned." + type = string + default = "" +} + +variable "security_group_id" { + description = "The ID of the security group Packer will associate with the builder to enable access" + type = string + default = null +} + +variable "subnet_id" { + description = "If using VPC, the ID of the subnet, such as subnet-12345def, where Packer will launch the EC2 instance. This field is required if you are using an non-default VPC" + type = string + default = null +} + +variable "root_volume_size_gb" { + type = number + default = 30 +} + +variable "ebs_delete_on_termination" { + description = "Indicates whether the EBS volume is deleted on instance termination." + type = bool + default = true +} + +variable "associate_public_ip_address" { + description = "If using a non-default VPC, there is no public IP address assigned to the EC2 instance. If you specified a public subnet, you probably want to set this to true. Otherwise the EC2 instance won't have access to the internet" + type = string + default = null +} + +variable "custom_shell_commands" { + description = "Additional commands to run on the EC2 instance, to customize the instance, like installing packages" + type = list(string) + default = [] +} + +variable "temporary_security_group_source_public_ip" { + description = "When enabled, use public IP of the host (obtained from https://checkip.amazonaws.com) as CIDR block to be authorized access to the instance, when packer is creating a temporary security group. Note: If you specify `security_group_id` then this input is ignored." + type = bool + default = false +} + +data "http" github_runner_release_json { + url = "https://api.github.com/repos/actions/runner/releases/latest" + request_headers = { + Accept = "application/vnd.github+json" + X-GitHub-Api-Version : "2022-11-28" + } +} + +locals { + runner_version = coalesce(var.runner_version, trimprefix(jsondecode(data.http.github_runner_release_json.body).tag_name, "v")) +} + +source "amazon-ebs" "githubrunner" { + ami_name = "github-runner-windows-core-2025-${formatdate("YYYYMMDDhhmm", timestamp())}" + communicator = "winrm" + instance_type = var.instance_type + iam_instance_profile = var.iam_instance_profile + region = var.region + security_group_id = var.security_group_id + subnet_id = var.subnet_id + associate_public_ip_address = var.associate_public_ip_address + temporary_security_group_source_public_ip = var.temporary_security_group_source_public_ip + + source_ami_filter { + filters = { + name = "Windows_Server-2025-English-Full-ECS_Optimized-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["amazon"] + } + tags = { + OS_Version = "windows-core-2025" + Release = "Latest" + Base_AMI_Name = "{{ .SourceAMIName }}" + } + user_data_file = "./bootstrap_win.ps1" + winrm_insecure = true + winrm_port = 5986 + winrm_use_ssl = true + winrm_username = "Administrator" + + launch_block_device_mappings { + device_name = "/dev/sda1" + volume_size = "${var.root_volume_size_gb}" + delete_on_termination = "${var.ebs_delete_on_termination}" + } +} + +build { + name = "githubactions-runner" + sources = [ + "source.amazon-ebs.githubrunner" + ] + + provisioner "file" { + content = templatefile("../start-runner.ps1", { + start_runner = templatefile("../../modules/runners/templates/start-runner.ps1", {}) + }) + destination = "C:\\start-runner.ps1" + } + + provisioner "powershell" { + inline = concat([ + templatefile("./windows-provisioner.ps1", { + action_runner_url = "https://github.com/actions/runner/releases/download/v${local.runner_version}/actions-runner-win-x64-${local.runner_version}.zip" + }) + ], var.custom_shell_commands) + } + post-processor "manifest" { + output = "manifest.json" + strip_path = true + } +} diff --git a/images/windows-core-2025/windows-provisioner.ps1 b/images/windows-core-2025/windows-provisioner.ps1 new file mode 100644 index 0000000000..aee18f600d --- /dev/null +++ b/images/windows-core-2025/windows-provisioner.ps1 @@ -0,0 +1,53 @@ +$ErrorActionPreference = "Continue" +$VerbosePreference = "Continue" + +# Install Chocolatey +[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 +$env:chocolateyUseWindowsCompression = 'true' +Invoke-WebRequest https://chocolatey.org/install.ps1 -UseBasicParsing | Invoke-Expression + +# Add Chocolatey to powershell profile +$ChocoProfileValue = @' +$ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1" +if (Test-Path($ChocolateyProfile)) { + Import-Module "$ChocolateyProfile" +} + +refreshenv +'@ +# Write it to the $profile location +Set-Content -Path "$PsHome\Microsoft.PowerShell_profile.ps1" -Value $ChocoProfileValue -Force +# Source it +. "$PsHome\Microsoft.PowerShell_profile.ps1" + +refreshenv + +Write-Host "Installing cloudwatch agent..." +Invoke-WebRequest -Uri https://s3.amazonaws.com/amazoncloudwatch-agent/windows/amd64/latest/amazon-cloudwatch-agent.msi -OutFile C:\amazon-cloudwatch-agent.msi +$cloudwatchParams = '/i', 'C:\amazon-cloudwatch-agent.msi', '/qn', '/L*v', 'C:\CloudwatchInstall.log' +Start-Process "msiexec.exe" $cloudwatchParams -Wait -NoNewWindow +Remove-Item C:\amazon-cloudwatch-agent.msi + +# Install dependent tools +Write-Host "Installing additional development tools" +choco install git awscli -y +refreshenv + +Write-Host "Creating actions-runner directory for the GH Action installation" +New-Item -ItemType Directory -Path C:\actions-runner ; Set-Location C:\actions-runner + +Write-Host "Downloading the GH Action runner from ${action_runner_url}" +Invoke-WebRequest -Uri ${action_runner_url} -OutFile actions-runner.zip + +Write-Host "Un-zip action runner" +Expand-Archive -Path actions-runner.zip -DestinationPath . + +Write-Host "Delete zip file" +Remove-Item actions-runner.zip + +$action = New-ScheduledTaskAction -WorkingDirectory "C:\actions-runner" -Execute "PowerShell.exe" -Argument "-File C:\start-runner.ps1" +$trigger = New-ScheduledTaskTrigger -AtStartup +Register-ScheduledTask -TaskName "runnerinit" -Action $action -Trigger $trigger -User System -RunLevel Highest -Force + +Write-Host "Running EC2Launch v2 to signal instance ready..." +& "$${env:ProgramFiles}\Amazon\EC2Launch\EC2Launch.exe" run \ No newline at end of file From d58df0042ef930d4eb5e6998261e47cad352335d Mon Sep 17 00:00:00 2001 From: Jon Anderson <1361123+jonanderson10@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:35:20 -0800 Subject: [PATCH 3/6] feat: latest instance types We don't like waiting for our AMIs to finish building :) --- images/linux-al2023/github_agent.linux.pkr.hcl | 2 +- images/ubuntu-focal/github_agent.ubuntu.pkr.hcl | 2 +- images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl | 2 +- images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl | 2 +- images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl | 2 +- images/ubuntu-noble/github_agent.ubuntu.pkr.hcl | 2 +- images/windows-core-2019/github_agent.windows.pkr.hcl | 2 +- images/windows-core-2022/github_agent.windows.pkr.hcl | 2 +- images/windows-core-2025/github_agent.windows.pkr.hcl | 4 ++-- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/images/linux-al2023/github_agent.linux.pkr.hcl b/images/linux-al2023/github_agent.linux.pkr.hcl index de72a9466f..c5b444f575 100644 --- a/images/linux-al2023/github_agent.linux.pkr.hcl +++ b/images/linux-al2023/github_agent.linux.pkr.hcl @@ -39,7 +39,7 @@ variable "associate_public_ip_address" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "m3.medium" + default = "c8i-flex.large" } variable "iam_instance_profile" { diff --git a/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl b/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl index 9712417274..2f556109bc 100644 --- a/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-focal/github_agent.ubuntu.pkr.hcl @@ -39,7 +39,7 @@ variable "associate_public_ip_address" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "t3.medium" + default = "c8i-flex.large" } variable "iam_instance_profile" { diff --git a/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl b/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl index 1536eb5784..671a2e4ebd 100644 --- a/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-jammy-arm64/github_agent.ubuntu.pkr.hcl @@ -39,7 +39,7 @@ variable "associate_public_ip_address" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "t4g.small" + default = "c8g.large" } variable "iam_instance_profile" { diff --git a/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl b/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl index be23a256ca..3234ced354 100644 --- a/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-jammy/github_agent.ubuntu.pkr.hcl @@ -39,7 +39,7 @@ variable "associate_public_ip_address" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "t3.medium" + default = "c8i-flex.large" } variable "iam_instance_profile" { diff --git a/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl b/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl index fd051ae0d6..f82e49042f 100644 --- a/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl @@ -39,7 +39,7 @@ variable "associate_public_ip_address" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "t4g.small" + default = "c8g.large" } variable "iam_instance_profile" { diff --git a/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl b/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl index b0f42277f0..4e2e5390b8 100644 --- a/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl @@ -39,7 +39,7 @@ variable "associate_public_ip_address" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "t3.medium" + default = " c8i-flex.large" } variable "iam_instance_profile" { diff --git a/images/windows-core-2019/github_agent.windows.pkr.hcl b/images/windows-core-2019/github_agent.windows.pkr.hcl index e27ad4a2bc..fde7b2d81b 100644 --- a/images/windows-core-2019/github_agent.windows.pkr.hcl +++ b/images/windows-core-2019/github_agent.windows.pkr.hcl @@ -21,7 +21,7 @@ variable "region" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "t3a.medium" + default = "c8i-flex.xlarge" } variable "iam_instance_profile" { diff --git a/images/windows-core-2022/github_agent.windows.pkr.hcl b/images/windows-core-2022/github_agent.windows.pkr.hcl index 0a85595ec5..4b2f1717c0 100644 --- a/images/windows-core-2022/github_agent.windows.pkr.hcl +++ b/images/windows-core-2022/github_agent.windows.pkr.hcl @@ -21,7 +21,7 @@ variable "region" { variable "instance_type" { description = "The instance type Packer will use for the builder" type = string - default = "m4.xlarge" + default = "c8i-flex.xlarge" } variable "iam_instance_profile" { diff --git a/images/windows-core-2025/github_agent.windows.pkr.hcl b/images/windows-core-2025/github_agent.windows.pkr.hcl index 5729575ac5..598bbe6774 100644 --- a/images/windows-core-2025/github_agent.windows.pkr.hcl +++ b/images/windows-core-2025/github_agent.windows.pkr.hcl @@ -19,9 +19,9 @@ variable "region" { } variable "instance_type" { - description = "The instance type Packer will use for the builder" + description = "The instance type Packer will use for the builder, requires UEFI/nitro instance support" type = string - default = "m4.xlarge" + default = "c8i-flex.xlarge" } variable "iam_instance_profile" { From f9547ae414c0fd96deec56ff80d842f7dccc3206 Mon Sep 17 00:00:00 2001 From: Jon Anderson <1361123+jonanderson10@users.noreply.github.com> Date: Fri, 14 Nov 2025 15:41:33 -0800 Subject: [PATCH 4/6] fix: add sleep for dpkg lock issue --- images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl | 5 ++++- images/ubuntu-noble/github_agent.ubuntu.pkr.hcl | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl b/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl index f82e49042f..8c766bcb19 100644 --- a/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-noble-arm64/github_agent.ubuntu.pkr.hcl @@ -153,6 +153,7 @@ build { ] inline = concat([ "sudo cloud-init status --wait", + "sleep 5", # Adding this to give time for dpkg lock to be released "sudo apt-get update", "sudo apt-get -y install ca-certificates curl gnupg lsb-release", "sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg", @@ -166,8 +167,10 @@ build { "sudo dpkg -i amazon-cloudwatch-agent.deb", "sudo systemctl restart amazon-cloudwatch-agent", "sudo curl -f https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip -o awscliv2.zip", - "unzip awscliv2.zip", + "unzip -q awscliv2.zip", + "sleep 5", # Adding this to give time for dpkg lock to be released "sudo ./aws/install", + "sleep 5", # Adding this to give time for dpkg lock to be released ], var.custom_shell_commands) } diff --git a/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl b/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl index 4e2e5390b8..ebf75b6296 100644 --- a/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl +++ b/images/ubuntu-noble/github_agent.ubuntu.pkr.hcl @@ -154,6 +154,7 @@ build { inline = concat([ "sudo cloud-init status --wait", "sudo apt-get -y update", + "sleep 5", # Adding this to give time for dpkg lock to be released "sudo apt-get -y install ca-certificates curl gnupg lsb-release", "sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg", "echo deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null", @@ -166,8 +167,10 @@ build { "sudo dpkg -i amazon-cloudwatch-agent.deb", "sudo systemctl restart amazon-cloudwatch-agent", "sudo curl -f https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip -o awscliv2.zip", - "unzip awscliv2.zip", + "unzip -q awscliv2.zip", + "sleep 5", # Adding this to give time for dpkg lock to be released "sudo ./aws/install", + "sleep 5", # Adding this to give time for dpkg lock to be released ], var.custom_shell_commands) } From 2cb2a8735d50613f63dd6e8c689ea466b02ccf02 Mon Sep 17 00:00:00 2001 From: Jon Anderson <1361123+jonanderson10@users.noreply.github.com> Date: Tue, 18 Nov 2025 14:52:29 -0800 Subject: [PATCH 5/6] fix: use pwsh to download gh runner --- images/windows-core-2025/windows-provisioner.ps1 | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/images/windows-core-2025/windows-provisioner.ps1 b/images/windows-core-2025/windows-provisioner.ps1 index aee18f600d..4d45d8cee8 100644 --- a/images/windows-core-2025/windows-provisioner.ps1 +++ b/images/windows-core-2025/windows-provisioner.ps1 @@ -30,14 +30,22 @@ Remove-Item C:\amazon-cloudwatch-agent.msi # Install dependent tools Write-Host "Installing additional development tools" -choco install git awscli -y +choco install git awscli powershell-core -y refreshenv Write-Host "Creating actions-runner directory for the GH Action installation" New-Item -ItemType Directory -Path C:\actions-runner ; Set-Location C:\actions-runner Write-Host "Downloading the GH Action runner from ${action_runner_url}" -Invoke-WebRequest -Uri ${action_runner_url} -OutFile actions-runner.zip +# Direct download with WebClient to prevent forced disconnects +$downloadCommand = @" +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +`$webClient = New-Object System.Net.WebClient +`$webClient.Headers.Add('User-Agent', 'PowerShell/7.0') +`$webClient.DownloadFile('${action_runner_url}', 'actions-runner.zip') +`$webClient.Dispose() +"@ +pwsh -Command $downloadCommand Write-Host "Un-zip action runner" Expand-Archive -Path actions-runner.zip -DestinationPath . From 6dee0290485aa63fac4e2d787cf660d10b1940f1 Mon Sep 17 00:00:00 2001 From: Jon Anderson <1361123+jonanderson10@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:06:58 -0800 Subject: [PATCH 6/6] fix: use longer ami timeouts for windows --- images/windows-core-2022/github_agent.windows.pkr.hcl | 4 ++++ images/windows-core-2025/github_agent.windows.pkr.hcl | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/images/windows-core-2022/github_agent.windows.pkr.hcl b/images/windows-core-2022/github_agent.windows.pkr.hcl index 4b2f1717c0..6958bbdc29 100644 --- a/images/windows-core-2022/github_agent.windows.pkr.hcl +++ b/images/windows-core-2022/github_agent.windows.pkr.hcl @@ -93,6 +93,10 @@ source "amazon-ebs" "githubrunner" { subnet_id = var.subnet_id associate_public_ip_address = var.associate_public_ip_address temporary_security_group_source_public_ip = var.temporary_security_group_source_public_ip + aws_polling { + delay_seconds = 60 + max_attempts = 120 + } source_ami_filter { filters = { diff --git a/images/windows-core-2025/github_agent.windows.pkr.hcl b/images/windows-core-2025/github_agent.windows.pkr.hcl index 598bbe6774..d0493b217b 100644 --- a/images/windows-core-2025/github_agent.windows.pkr.hcl +++ b/images/windows-core-2025/github_agent.windows.pkr.hcl @@ -93,6 +93,10 @@ source "amazon-ebs" "githubrunner" { subnet_id = var.subnet_id associate_public_ip_address = var.associate_public_ip_address temporary_security_group_source_public_ip = var.temporary_security_group_source_public_ip + aws_polling { + delay_seconds = 60 + max_attempts = 120 + } source_ami_filter { filters = {