From 790798402be060fe5c3b190c00782eeca8456c11 Mon Sep 17 00:00:00 2001 From: Jacob Burroughs Date: Wed, 5 Jan 2022 05:37:01 -0600 Subject: [PATCH] fix: Add config for windows ami (#1525) * Add config for windows ami * Update packer build to validate windows --- .github/workflows/packer-build.yml | 7 +- examples/prebuilt/README.md | 12 +++- examples/prebuilt/main.tf | 13 ++-- examples/prebuilt/providers.tf | 2 +- examples/prebuilt/variables.tf | 15 +++++ examples/prebuilt/vpc.tf | 2 +- images/README.md | 2 +- images/install-runner.ps1 | 8 +++ images/start-runner.ps1 | 3 + images/windows-core-2019/bootstrap_win.ps1 | 38 +++++++++++ .../github_agent.windows.pkr.hcl | 66 +++++++++++++++++++ .../windows-core-2019/windows-provisioner.ps1 | 52 +++++++++++++++ 12 files changed, 208 insertions(+), 12 deletions(-) create mode 100644 images/install-runner.ps1 create mode 100644 images/start-runner.ps1 create mode 100644 images/windows-core-2019/bootstrap_win.ps1 create mode 100644 images/windows-core-2019/github_agent.windows.pkr.hcl create mode 100644 images/windows-core-2019/windows-provisioner.ps1 diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index 96f3de47a3..3ccac79f6a 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -18,9 +18,12 @@ jobs: runs-on: ubuntu-latest container: image: hashicorp/packer:1.7.8 + strategy: + matrix: + image: ["linux-amzn2", "windows-core-2019"] defaults: run: - working-directory: images/linux-amzn2 + working-directory: images/${{ matrix.image }} steps: - name: "Checkout" uses: actions/checkout@v2 @@ -28,7 +31,7 @@ jobs: - name: packer init run: packer init . - - name: check terraform formatting + - name: check packer formatting run: packer fmt -recursive -check=true . - name: packer validate diff --git a/examples/prebuilt/README.md b/examples/prebuilt/README.md index 3a560fb80d..5065965d85 100644 --- a/examples/prebuilt/README.md +++ b/examples/prebuilt/README.md @@ -4,7 +4,17 @@ This module shows how to create GitHub action runners using a prebuilt AMI for t ## Usages -Steps for the full setup, such as creating a GitHub app can be found in the root module's [README](../../README.md). +Steps for the full setup, such as creating a GitHub app can be found in the root module's [README](../../README.md). + +## Variables + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [ami\_filter](#input\_ami\_filter) | The amis to search. Use the default for the provided amazon linux image, `github-runner-windows-core-2019-*` for the provided widnows image | `string` | `github-runner-amzn2-x86_64-2021*` | no | +| [github\_app\_key\_base64](#input\_github\_app\_key\_base64) | The base64 encoded private key you downloaded from GitHub when creating the app | `string` | | yes | +| [github\_app\_id](#input\_github\_app\_id) | The id of the app you created on GitHub | `string` | | yes | +| [region](#input\_region) | The target aws region | `string` | `eu-west-1` | no | +| [runner\_os](#input\_runner\_os) | The os of the image, either `linux` or `windows` | `string` | `linux` | no | ### Lambdas diff --git a/examples/prebuilt/main.tf b/examples/prebuilt/main.tf index 7e417f460f..710f6c1f60 100644 --- a/examples/prebuilt/main.tf +++ b/examples/prebuilt/main.tf @@ -1,6 +1,5 @@ locals { environment = "prebuilt" - aws_region = "eu-west-1" } resource "random_id" "random" { @@ -12,7 +11,7 @@ data "aws_caller_identity" "current" {} module "runners" { source = "../../" create_service_linked_role_spot = true - aws_region = local.aws_region + aws_region = var.aws_region vpc_id = module.vpc.vpc_id subnet_ids = module.vpc.private_subnets @@ -24,15 +23,17 @@ module "runners" { webhook_secret = random_id.random.hex } - webhook_lambda_zip = "../../lambda_output/webhook.zip" - runner_binaries_syncer_lambda_zip = "../../lambda_output/runner-binaries-syncer.zip" - runners_lambda_zip = "../../lambda_output/runners.zip" + webhook_lambda_zip = "lambdas-download/webhook.zip" + runner_binaries_syncer_lambda_zip = "lambdas-download/runner-binaries-syncer.zip" + runners_lambda_zip = "lambdas-download/runners.zip" runner_extra_labels = "default,example" + runner_os = var.runner_os + # configure your pre-built AMI enabled_userdata = false - ami_filter = { name = ["github-runner-amzn2-x86_64-2021*"] } + ami_filter = { name = [var.ami_name_filter] } ami_owners = [data.aws_caller_identity.current.account_id] # enable access to the runners via SSM diff --git a/examples/prebuilt/providers.tf b/examples/prebuilt/providers.tf index b6c81d5415..c9d7ccbdea 100644 --- a/examples/prebuilt/providers.tf +++ b/examples/prebuilt/providers.tf @@ -1,3 +1,3 @@ provider "aws" { - region = local.aws_region + region = var.aws_region } diff --git a/examples/prebuilt/variables.tf b/examples/prebuilt/variables.tf index 69dcd0c61c..3e97a3144c 100644 --- a/examples/prebuilt/variables.tf +++ b/examples/prebuilt/variables.tf @@ -2,3 +2,18 @@ variable "github_app_key_base64" {} variable "github_app_id" {} + +variable "runner_os" { + type = string + default = "linux" +} + +variable "ami_name_filter" { + type = string + default = "github-runner-amzn2-x86_64-2021*" +} + +variable "aws_region" { + type = string + default = "eu-west-1" +} \ No newline at end of file diff --git a/examples/prebuilt/vpc.tf b/examples/prebuilt/vpc.tf index a7d21422f1..1ac37cb4c8 100644 --- a/examples/prebuilt/vpc.tf +++ b/examples/prebuilt/vpc.tf @@ -2,6 +2,6 @@ module "vpc" { source = "git::https://github.com/philips-software/terraform-aws-vpc.git?ref=2.2.0" environment = local.environment - aws_region = local.aws_region + aws_region = var.aws_region create_private_hosted_zone = false } diff --git a/images/README.md b/images/README.md index 172f97d0a1..a3cbe7bec9 100644 --- a/images/README.md +++ b/images/README.md @@ -4,7 +4,7 @@ The images inside this folder are pre-built images designed to shorten the boot These images share the same scripting as used in the user-data mechanism in `/modules/runners/templates/`. We use a `tempaltefile` mechanism to insert the relevant script fragments into the scripts used for provisioning the images. -The example in `linux-amzn2` also uploads a `start-runner.sh` script that uses the exact same startup process as used in the user-data mechanism. This means that the image created here does not need any extra scripts injected or changes to boot up and connect to GH. +The examples in `linux-amzn2` and `windows-core-2019` also upload a `start-runner` script that uses the exact same startup process as used in the user-data mechanism. This means that the image created here does not need any extra scripts injected or changes to boot up and connect to GH. ## Building your own diff --git a/images/install-runner.ps1 b/images/install-runner.ps1 new file mode 100644 index 0000000000..e042333f00 --- /dev/null +++ b/images/install-runner.ps1 @@ -0,0 +1,8 @@ +#!/bin/bash -e + +user_name=ec2-user + +## This wrapper file re-uses scripts in the /modules/runners/templates directory +## of this repo. These are the same that are used by the user_data functionality +## to bootstrap the instance if it is started from an existing AMI. +${install_runner} \ No newline at end of file diff --git a/images/start-runner.ps1 b/images/start-runner.ps1 new file mode 100644 index 0000000000..e4b6b85e51 --- /dev/null +++ b/images/start-runner.ps1 @@ -0,0 +1,3 @@ +Start-Transcript -Path "C:\runner-startup.log" -Append +${start_runner} +Stop-Transcript diff --git a/images/windows-core-2019/bootstrap_win.ps1 b/images/windows-core-2019/bootstrap_win.ps1 new file mode 100644 index 0000000000..3cba59a089 --- /dev/null +++ b/images/windows-core-2019/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-2019/github_agent.windows.pkr.hcl b/images/windows-core-2019/github_agent.windows.pkr.hcl new file mode 100644 index 0000000000..d15d462476 --- /dev/null +++ b/images/windows-core-2019/github_agent.windows.pkr.hcl @@ -0,0 +1,66 @@ +packer { + required_plugins { + amazon = { + version = ">= 0.0.2" + source = "github.com/hashicorp/amazon" + } + } +} + +variable "action_runner_url" { + description = "The URL to the tarball of the action runner" + type = string + default = "https://github.com/actions/runner/releases/download/v2.285.1/actions-runner-win-x64-2.285.1.zip" +} + +variable "region" { + description = "The region to build the image in" + type = string + default = "eu-west-1" +} + +source "amazon-ebs" "githubrunner" { + ami_name = "github-runner-windows-core-2019-${formatdate("YYYYMMDDhhmm", timestamp())}" + communicator = "winrm" + instance_type = "t3a.medium" + region = var.region + source_ami_filter { + filters = { + name = "Windows_Server-2019-English-Core-ContainersLatest-*" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["amazon"] + } + tags = { + OS_Version = "windows-core-2019" + 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" +} + +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 = [templatefile("./windows-provisioner.ps1", { + action_runner_url = var.action_runner_url + })] + } +} \ No newline at end of file diff --git a/images/windows-core-2019/windows-provisioner.ps1 b/images/windows-core-2019/windows-provisioner.ps1 new file mode 100644 index 0000000000..a192d7e983 --- /dev/null +++ b/images/windows-core-2019/windows-provisioner.ps1 @@ -0,0 +1,52 @@ +$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 installtion" +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 + +C:\ProgramData\Amazon\EC2-Windows\Launch\Scripts\InitializeInstance.ps1 -Schedule \ No newline at end of file