From ccf78b38e57b09163d132c21bb51d4c14f949cfd Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 25 Nov 2021 14:16:41 +0100 Subject: [PATCH 01/36] Initial creation of runner image --- .editorconfig | 2 ++ images/github_agent.linux.pkr.hcl | 52 +++++++++++++++++++++++++++++++ images/install-runner.sh | 30 ++++++++++++++++++ images/startup.sh | 40 ++++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 .editorconfig create mode 100644 images/github_agent.linux.pkr.hcl create mode 100644 images/install-runner.sh create mode 100644 images/startup.sh diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..270106b1fa --- /dev/null +++ b/.editorconfig @@ -0,0 +1,2 @@ +[*] +end_of_line = lf diff --git a/images/github_agent.linux.pkr.hcl b/images/github_agent.linux.pkr.hcl new file mode 100644 index 0000000000..182f2b9ff6 --- /dev/null +++ b/images/github_agent.linux.pkr.hcl @@ -0,0 +1,52 @@ +packer { + required_plugins { + amazon = { + version = ">= 0.0.2" + source = "github.com/hashicorp/amazon" + } + } +} + +source "amazon-ebs" "githubrunner" { + ami_name = "github-runner-amzn2-${ formatdate("YYYY-MM-DDhhmmssZ", timestamp()) }" + instance_type = "m3.medium" + region = "eu-west-1" + source_ami_filter { + filters = { + name = "amzn2-ami-hvm-2.*-x86_64-ebs" + root-device-type = "ebs" + virtualization-type = "hvm" + } + most_recent = true + owners = ["137112412989"] + } + ssh_username = "ec2-user" + tags = { + OS_Version = "amzn2" + Release = "Latest" + Base_AMI_Name = "{{ .SourceAMIName }}" + } +} + +build { + name = "githubactions-runner" + sources = [ + "source.amazon-ebs.githubrunner" + ] + provisioner "shell" { + environment_vars = [] + inline = [ + "sudo yum update -y", + "sudo yum install -y amazon-cloudwatch-agent curl jq git", + "sudo amazon-linux-extras install docker", + "sudo service docker start", + "sudo usermod -a -G docker ec2-user", + ] + } + + provisioner "shell" { + environment_vars = [] + script = "./install-runner.sh" + } + +} \ No newline at end of file diff --git a/images/install-runner.sh b/images/install-runner.sh new file mode 100644 index 0000000000..241e0993eb --- /dev/null +++ b/images/install-runner.sh @@ -0,0 +1,30 @@ +#!/bin/bash -e +set -e + +USER_NAME=ec2-user + +echo "Creating actions-runner directory for the GH Action installtion" +cd /home/"$USER_NAME" +mkdir actions-runner && cd actions-runner + +file_name="actions-runner.tar.gz" +echo "Downloading the GH Action runner to $file_name" +curl -o $file_name -L https://github.com/actions/runner/releases/download/v2.284.0/actions-runner-linux-x64-2.284.0.tar.gz + +# echo "1ddfd7bbd3f2b8f5684a7d88d6ecb6de3cb2281a2a359543a018cc6e177067fc actions-runner-linux-x64-2.284.0.tar.gz" | shasum -a 256 -c +echo "Un-tar action runner" +tar xzf ./$file_name +echo "Delete tar file" +rm -rf $file_name + +echo "export RUNNER_ALLOW_RUNASROOT=1" +export RUNNER_ALLOW_RUNASROOT=1 + +os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release) +if [[ "$os_id" =~ ^ubuntu.* ]]; then + echo "Installing dependencies" + ./bin/installdependencies.sh +fi + +echo "Set file ownership of action runner" +chown -R "$USER_NAME":"$USER_NAME" . \ No newline at end of file diff --git a/images/startup.sh b/images/startup.sh new file mode 100644 index 0000000000..6e0fa2eb7d --- /dev/null +++ b/images/startup.sh @@ -0,0 +1,40 @@ +#!/bin/bash -e + +%{ if enable_cloudwatch_agent ~} +amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:${ssm_key_cloudwatch_agent_config} +%{ endif ~} + + +cd /home/$USER_NAME/actions-runner + +TOKEN=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") +REGION=$(curl -f -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) +INSTANCE_ID=$(curl -f -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/instance-id) + +echo wait for configuration +while [[ $(aws ssm get-parameters --names ${environment}-$INSTANCE_ID --with-decryption --region $REGION | jq -r ".Parameters | .[0] | .Value") == null ]]; do + echo Waiting for configuration ... + sleep 1 +done +CONFIG=$(aws ssm get-parameters --names ${environment}-$INSTANCE_ID --with-decryption --region $REGION | jq -r ".Parameters | .[0] | .Value") +aws ssm delete-parameter --name ${environment}-$INSTANCE_ID --region $REGION + +export RUNNER_ALLOW_RUNASROOT=1 +os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release) +if [[ "$os_id" =~ ^ubuntu.* ]]; then + ./bin/installdependencies.sh +fi + +./config.sh --unattended --name $INSTANCE_ID --work "_work" $CONFIG + +chown -R $USER_NAME:$USER_NAME . +OVERWRITE_SERVICE_USER=${run_as_root_user} +SERVICE_USER=$${OVERWRITE_SERVICE_USER:-$USER_NAME} + + + +# TODO: svc.sh start replaced by run.sh and once stopped / exitted the instance terminated +sudo -u $SERVICE_USER -- ./run.sh +#service awslogsd stop + +aws ec2 terminate-instances --instance-ids $INSTANCE_ID --region $REGION From 563391bc5eff41424339f51ad9075e9e3f548392 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 25 Nov 2021 18:44:39 +0100 Subject: [PATCH 02/36] Refactored startup script and added it to the per-boot folder --- images/github_agent.linux.pkr.hcl | 30 +++++++++----- images/startup.sh | 67 ++++++++++++++++++------------- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/images/github_agent.linux.pkr.hcl b/images/github_agent.linux.pkr.hcl index 182f2b9ff6..093466d753 100644 --- a/images/github_agent.linux.pkr.hcl +++ b/images/github_agent.linux.pkr.hcl @@ -8,7 +8,7 @@ packer { } source "amazon-ebs" "githubrunner" { - ami_name = "github-runner-amzn2-${ formatdate("YYYY-MM-DDhhmmssZ", timestamp()) }" + ami_name = "github-runner-amzn2-${formatdate("YYYYMMDDhhmm", timestamp())}" instance_type = "m3.medium" region = "eu-west-1" source_ami_filter { @@ -22,9 +22,9 @@ source "amazon-ebs" "githubrunner" { } ssh_username = "ec2-user" tags = { - OS_Version = "amzn2" - Release = "Latest" - Base_AMI_Name = "{{ .SourceAMIName }}" + OS_Version = "amzn2" + Release = "Latest" + Base_AMI_Name = "{{ .SourceAMIName }}" } } @@ -33,20 +33,32 @@ build { sources = [ "source.amazon-ebs.githubrunner" ] - provisioner "shell" { + provisioner "shell" { environment_vars = [] - inline = [ + inline = [ "sudo yum update -y", "sudo yum install -y amazon-cloudwatch-agent curl jq git", "sudo amazon-linux-extras install docker", "sudo service docker start", "sudo usermod -a -G docker ec2-user", ] - } + } provisioner "shell" { environment_vars = [] - script = "./install-runner.sh" - } + script = "./install-runner.sh" + } + + provisioner "file" { + source = "startup.sh" + destination = "/tmp/startup.sh" + } + + provisioner "shell" { + inline = [ + "sudo mv /tmp/startup.sh /var/lib/cloud/scripts/per-boot/startup.sh", + "sudo chmod +x /var/lib/cloud/scripts/per-boot/startup.sh", + ] + } } \ No newline at end of file diff --git a/images/startup.sh b/images/startup.sh index 6e0fa2eb7d..700da1a4f7 100644 --- a/images/startup.sh +++ b/images/startup.sh @@ -1,40 +1,53 @@ #!/bin/bash -e -%{ if enable_cloudwatch_agent ~} -amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:${ssm_key_cloudwatch_agent_config} -%{ endif ~} +user_name=ec2-user +if [[ -z "$ENVIRONMENT" ]]; then + echo "ENVIRONMENT is not set" + exit 1 +fi -cd /home/$USER_NAME/actions-runner +if [[ -n "$ENABLE_CLOUDWATCH_AGENT" ]]; then + if [[ -z "$SSM_KEY_CLOUDWATCH_AGENT_CONFIG" ]]; then + echo "Cloudwatch is enabled with ENABLE_CLOUDWATCH_AGENT but SSM_KEY_CLOUDWATCH_AGENT_CONFIG is not set" + exit 1 + fi + echo "Cloudwatch is enabled" + amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:"$SSM_KEY_CLOUDWATCH_AGENT_CONFIG" +fi -TOKEN=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") -REGION=$(curl -f -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) -INSTANCE_ID=$(curl -f -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/instance-id) +cd /home/$user_name/actions-runner -echo wait for configuration -while [[ $(aws ssm get-parameters --names ${environment}-$INSTANCE_ID --with-decryption --region $REGION | jq -r ".Parameters | .[0] | .Value") == null ]]; do - echo Waiting for configuration ... - sleep 1 -done -CONFIG=$(aws ssm get-parameters --names ${environment}-$INSTANCE_ID --with-decryption --region $REGION | jq -r ".Parameters | .[0] | .Value") -aws ssm delete-parameter --name ${environment}-$INSTANCE_ID --region $REGION +echo "Retrieving TOKEN from AWS API" +token=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") -export RUNNER_ALLOW_RUNASROOT=1 -os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release) -if [[ "$os_id" =~ ^ubuntu.* ]]; then - ./bin/installdependencies.sh -fi +echo "Retrieving REGION from AWS API" +region=$(curl -f -H "X-aws-ec2-metadata-token: $token" -v http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) -./config.sh --unattended --name $INSTANCE_ID --work "_work" $CONFIG +echo "Retrieving INSTANCE_ID from AWS API" +instance_id=$(curl -f -H "X-aws-ec2-metadata-token: $token" -v http://169.254.169.254/latest/meta-data/instance-id) -chown -R $USER_NAME:$USER_NAME . -OVERWRITE_SERVICE_USER=${run_as_root_user} -SERVICE_USER=$${OVERWRITE_SERVICE_USER:-$USER_NAME} +echo "Get GH Runner token from AWS SSM" +config=$(aws ssm get-parameters --names "$ENVIRONMENT"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") +while [[ -z "$config" ]]; do + echo "Waiting for GH Runner token to become available in AWS SSM" + sleep 1 + config=$(aws ssm get-parameters --names "$ENVIRONMENT"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") +done +echo "Delete GH Runner token from AWS SSM" +aws ssm delete-parameter --name "$ENVIRONMENT"-"$instance_id" --region "$region" -# TODO: svc.sh start replaced by run.sh and once stopped / exitted the instance terminated -sudo -u $SERVICE_USER -- ./run.sh -#service awslogsd stop +echo "Configure GH Runner" +./config.sh --unattended --name "$instance_id" --work "_work" "$config" + +service_user=${RUN_AGENT_AS_USER:-$user_name} -aws ec2 terminate-instances --instance-ids $INSTANCE_ID --region $REGION +echo "Start the runner as user $service_user" +sudo -u "$service_user" -- ./run.sh +echo "Runner has finished" + +#service awslogsd stop +echo "Terminating instance" +aws ec2 terminate-instances --instance-ids "$instance_id" --region "$region" From d8da7a40b4a1350e268dcf5ebd8cd3fff51f7354 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Sat, 27 Nov 2021 15:36:53 +0100 Subject: [PATCH 03/36] Make the runner location a variable So we can pass the runner version in at packer build time if we want to update the runner version. --- images/github_agent.linux.pkr.hcl | 10 +++++++++- images/install-runner.sh | 18 +++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/images/github_agent.linux.pkr.hcl b/images/github_agent.linux.pkr.hcl index 093466d753..926fe1c86d 100644 --- a/images/github_agent.linux.pkr.hcl +++ b/images/github_agent.linux.pkr.hcl @@ -7,6 +7,12 @@ packer { } } +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.284.0/actions-runner-linux-x64-2.284.0.tar.gz" +} + source "amazon-ebs" "githubrunner" { ami_name = "github-runner-amzn2-${formatdate("YYYYMMDDhhmm", timestamp())}" instance_type = "m3.medium" @@ -45,7 +51,9 @@ build { } provisioner "shell" { - environment_vars = [] + environment_vars = [ + "RUNNER_TARBALL_URL=${var.action_runner_url}" + ] script = "./install-runner.sh" } diff --git a/images/install-runner.sh b/images/install-runner.sh index 241e0993eb..7b5cfc3ce3 100644 --- a/images/install-runner.sh +++ b/images/install-runner.sh @@ -1,17 +1,21 @@ #!/bin/bash -e set -e -USER_NAME=ec2-user +if [[ -z "$RUNNER_TARBALL_URL" ]]; then + echo "RUNNER_TARBALL_URL is not set" + exit 1 +fi + +user_name=ec2-user +file_name="actions-runner.tar.gz" echo "Creating actions-runner directory for the GH Action installtion" -cd /home/"$USER_NAME" +cd /home/"$user_name" mkdir actions-runner && cd actions-runner -file_name="actions-runner.tar.gz" -echo "Downloading the GH Action runner to $file_name" -curl -o $file_name -L https://github.com/actions/runner/releases/download/v2.284.0/actions-runner-linux-x64-2.284.0.tar.gz +echo "Downloading the GH Action runner from $RUNNER_TARBALL_URL to $file_name" +curl -o $file_name -L "$RUNNER_TARBALL_URL" -# echo "1ddfd7bbd3f2b8f5684a7d88d6ecb6de3cb2281a2a359543a018cc6e177067fc actions-runner-linux-x64-2.284.0.tar.gz" | shasum -a 256 -c echo "Un-tar action runner" tar xzf ./$file_name echo "Delete tar file" @@ -27,4 +31,4 @@ if [[ "$os_id" =~ ^ubuntu.* ]]; then fi echo "Set file ownership of action runner" -chown -R "$USER_NAME":"$USER_NAME" . \ No newline at end of file +chown -R "$user_name":"$user_name" . \ No newline at end of file From 9a4bb2fb2fd796dca812ab38d04d109b674d5ce1 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Sat, 27 Nov 2021 15:39:23 +0100 Subject: [PATCH 04/36] Retrieve external config setting via tags Retrieve the required config via the instance tags so we dont have to pass in and set environment on the instance in an awkward way. --- images/startup.sh | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/images/startup.sh b/images/startup.sh index 700da1a4f7..2513290fbe 100644 --- a/images/startup.sh +++ b/images/startup.sh @@ -1,47 +1,49 @@ #!/bin/bash -e +exec > >(tee /var/log/runner-startup.log | logger -t user-data -s 2>/dev/console) 2>&1 user_name=ec2-user -if [[ -z "$ENVIRONMENT" ]]; then - echo "ENVIRONMENT is not set" - exit 1 -fi - -if [[ -n "$ENABLE_CLOUDWATCH_AGENT" ]]; then - if [[ -z "$SSM_KEY_CLOUDWATCH_AGENT_CONFIG" ]]; then - echo "Cloudwatch is enabled with ENABLE_CLOUDWATCH_AGENT but SSM_KEY_CLOUDWATCH_AGENT_CONFIG is not set" - exit 1 - fi - echo "Cloudwatch is enabled" - amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:"$SSM_KEY_CLOUDWATCH_AGENT_CONFIG" -fi - cd /home/$user_name/actions-runner echo "Retrieving TOKEN from AWS API" token=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") -echo "Retrieving REGION from AWS API" region=$(curl -f -H "X-aws-ec2-metadata-token: $token" -v http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) +echo "Reteieved REGION from AWS API ($region)" -echo "Retrieving INSTANCE_ID from AWS API" instance_id=$(curl -f -H "X-aws-ec2-metadata-token: $token" -v http://169.254.169.254/latest/meta-data/instance-id) +echo "Reteieved INSTANCE_ID from AWS API ($instance_id)" + +tags=$(aws ec2 describe-tags --region "$region" --filters "Name=resource-id,Values=$instance_id") +echo "Retrieved tags from AWS API ($tags)" + +environment=$(echo "$tags" | jq '.Tags[] | select(.Key == "Environment") | .Value') +echo "Reteieved environment tag - ($environment)" + +enable_cloudwatch_agent=$(echo "$tags" | jq '.Tags[] | select(.Key == "enable_cloudwatch_agent") | .Value') +echo "Reteieved enable_cloudwatch_agent tag - ($enable_cloudwatch_agent)" + +if [[ -n "$enable_cloudwatch_agent" ]]; then + echo "Cloudwatch is enabled" + amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:"$environment-cloudwatch_agent_config_runner" +fi echo "Get GH Runner token from AWS SSM" -config=$(aws ssm get-parameters --names "$ENVIRONMENT"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") +config=$(aws ssm get-parameters --names "$environment"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") while [[ -z "$config" ]]; do echo "Waiting for GH Runner token to become available in AWS SSM" sleep 1 - config=$(aws ssm get-parameters --names "$ENVIRONMENT"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") + config=$(aws ssm get-parameters --names "$environment"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") done echo "Delete GH Runner token from AWS SSM" -aws ssm delete-parameter --name "$ENVIRONMENT"-"$instance_id" --region "$region" +aws ssm delete-parameter --name "$environment"-"$instance_id" --region "$region" echo "Configure GH Runner" ./config.sh --unattended --name "$instance_id" --work "_work" "$config" +# TODO this should also be passed via tags service_user=${RUN_AGENT_AS_USER:-$user_name} echo "Start the runner as user $service_user" From 45b4aaa52964873be4fd4cff0537e360fccbf8fe Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Sat, 27 Nov 2021 15:40:01 +0100 Subject: [PATCH 05/36] Enable tag based config Give the instance the permission to query its own tags and set the correct tags on the instance. --- modules/runners/main.tf | 13 +++++++++---- modules/runners/policies-runner.tf | 6 ++++++ .../policies/instance-describe-tags-policy.json | 10 ++++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 modules/runners/policies/instance-describe-tags-policy.json diff --git a/modules/runners/main.tf b/modules/runners/main.tf index de788f1b2f..d9954b259c 100644 --- a/modules/runners/main.tf +++ b/modules/runners/main.tf @@ -6,6 +6,9 @@ locals { { "Environment" = format("%s", var.environment) }, + { + "enable_cloudwatch_agent" = var.enable_cloudwatch_agent + }, var.tags, ) @@ -113,10 +116,12 @@ resource "aws_launch_template" "runner" { user_data = base64encode(templatefile(local.userdata_template, { - environment = var.environment - pre_install = var.userdata_pre_install - post_install = var.userdata_post_install - enable_cloudwatch_agent = var.enable_cloudwatch_agent + environment = var.environment + ## TODO I think we can still provide this tailoring functionality in this way but should make it more generic + pre_install = var.userdata_pre_install + post_install = var.userdata_post_install + enable_cloudwatch_agent = var.enable_cloudwatch_agent + # TODO Figure out if we need these here. We can pass them via Tags to the instance so we dont have to do trickey with env ssm_key_cloudwatch_agent_config = var.enable_cloudwatch_agent ? aws_ssm_parameter.cloudwatch_agent_config_runner[0].name : "" ghes_url = var.ghes_url ghes_ssl_verify = var.ghes_ssl_verify diff --git a/modules/runners/policies-runner.tf b/modules/runners/policies-runner.tf index 2aa62da8dd..3d649babff 100644 --- a/modules/runners/policies-runner.tf +++ b/modules/runners/policies-runner.tf @@ -41,6 +41,12 @@ resource "aws_iam_role_policy" "dist_bucket" { ) } +resource "aws_iam_role_policy" "describe_tags" { + name = "runner-describe-tags" + role = aws_iam_role.runner.name + policy = templatefile("${path.module}/policies/instance-describe-tags-policy.json", {}) +} + resource "aws_iam_role_policy_attachment" "managed_policies" { count = length(var.runner_iam_role_managed_policy_arns) role = aws_iam_role.runner.name diff --git a/modules/runners/policies/instance-describe-tags-policy.json b/modules/runners/policies/instance-describe-tags-policy.json new file mode 100644 index 0000000000..7f93d36246 --- /dev/null +++ b/modules/runners/policies/instance-describe-tags-policy.json @@ -0,0 +1,10 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "ec2:DescribeTags", + "Resource": "*" + } + ] +} \ No newline at end of file From 17e83d966243a156cf68ee724270d02cb9de4c57 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Sat, 27 Nov 2021 15:43:55 +0100 Subject: [PATCH 06/36] Add a CI job --- .github/workflows/packer-build.yml | 33 ++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 .github/workflows/packer-build.yml diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml new file mode 100644 index 0000000000..1eaf85ca6a --- /dev/null +++ b/.github/workflows/packer-build.yml @@ -0,0 +1,33 @@ +name: "Packer checks" +on: + push: + branches: + - master + - develop + pull_request: + paths: + - "images/**" + - ".github/workflows/packer-build.yml" + +env: + AWS_REGION: eu-west-1 + +jobs: + verify_packer: + name: Verify image + runs-on: ubuntu-latest + container: + image: hashicorp/packer:1.7.8 + steps: + - name: "Checkout" + uses: actions/checkout@v2 + + - name: packer init + run: packer init + + - name: check terraform formatting + run: packer fmt -recursive -check=true + continue-on-error: true + + - name: packer validate + run: packer validate From 64a8b59dc3a2b2be079acea509edca5a6719e0d1 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Sat, 27 Nov 2021 15:46:38 +0100 Subject: [PATCH 07/36] Fix the CI build --- .github/workflows/packer-build.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index 1eaf85ca6a..9fd28f174f 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -11,23 +11,26 @@ on: env: AWS_REGION: eu-west-1 - + jobs: verify_packer: name: Verify image runs-on: ubuntu-latest container: image: hashicorp/packer:1.7.8 + defaults: + run: + working-directory: images/ steps: - name: "Checkout" uses: actions/checkout@v2 - name: packer init - run: packer init + run: packer init . - name: check terraform formatting - run: packer fmt -recursive -check=true + run: packer fmt -recursive -check=true . continue-on-error: true - name: packer validate - run: packer validate + run: packer validate . From e8b35713db7024560b0d098a0b50801c5c3c626e Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Sat, 27 Nov 2021 15:48:07 +0100 Subject: [PATCH 08/36] Fix the formatting --- .github/workflows/packer-build.yml | 3 +-- images/github_agent.linux.pkr.hcl | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index 9fd28f174f..d294facd34 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -29,8 +29,7 @@ jobs: run: packer init . - name: check terraform formatting - run: packer fmt -recursive -check=true . - continue-on-error: true + run: packer fmt -recursive -check=true . - name: packer validate run: packer validate . diff --git a/images/github_agent.linux.pkr.hcl b/images/github_agent.linux.pkr.hcl index 926fe1c86d..94ddb68b07 100644 --- a/images/github_agent.linux.pkr.hcl +++ b/images/github_agent.linux.pkr.hcl @@ -9,8 +9,8 @@ packer { 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.284.0/actions-runner-linux-x64-2.284.0.tar.gz" + type = string + default = "https://github.com/actions/runner/releases/download/v2.284.0/actions-runner-linux-x64-2.284.0.tar.gz" } source "amazon-ebs" "githubrunner" { @@ -54,7 +54,7 @@ build { environment_vars = [ "RUNNER_TARBALL_URL=${var.action_runner_url}" ] - script = "./install-runner.sh" + script = "./install-runner.sh" } provisioner "file" { From 9d4e660ae93c4468b7fd50604eb6f2ce900953f5 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Mon, 29 Nov 2021 18:51:56 +0100 Subject: [PATCH 09/36] Retain user_data provisioning and remove duplication refactored to make sure user_data continues to work with minimal breaking changes. Use a single set of scripts shared between image and user_data provisioning. --- images/install-runner.sh | 33 ++--------- .../github_agent.linux.pkr.hcl | 25 ++++++-- images/start-runner.sh | 9 +++ modules/runners/main.tf | 59 ++++++++++--------- .../templates/install-config-runner.sh | 35 ----------- modules/runners/templates/install-runner.sh | 48 +++++++++++++++ .../runners/templates/start-runner.sh | 51 ++++++++++------ modules/runners/templates/user-data.sh | 12 ++-- 8 files changed, 147 insertions(+), 125 deletions(-) rename images/{ => linux-amzn2}/github_agent.linux.pkr.hcl (70%) create mode 100644 images/start-runner.sh delete mode 100644 modules/runners/templates/install-config-runner.sh create mode 100644 modules/runners/templates/install-runner.sh rename images/startup.sh => modules/runners/templates/start-runner.sh (57%) diff --git a/images/install-runner.sh b/images/install-runner.sh index 7b5cfc3ce3..6a043a2c79 100644 --- a/images/install-runner.sh +++ b/images/install-runner.sh @@ -1,34 +1,9 @@ #!/bin/bash -e set -e -if [[ -z "$RUNNER_TARBALL_URL" ]]; then - echo "RUNNER_TARBALL_URL is not set" - exit 1 -fi - user_name=ec2-user -file_name="actions-runner.tar.gz" - -echo "Creating actions-runner directory for the GH Action installtion" -cd /home/"$user_name" -mkdir actions-runner && cd actions-runner - -echo "Downloading the GH Action runner from $RUNNER_TARBALL_URL to $file_name" -curl -o $file_name -L "$RUNNER_TARBALL_URL" - -echo "Un-tar action runner" -tar xzf ./$file_name -echo "Delete tar file" -rm -rf $file_name - -echo "export RUNNER_ALLOW_RUNASROOT=1" -export RUNNER_ALLOW_RUNASROOT=1 - -os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release) -if [[ "$os_id" =~ ^ubuntu.* ]]; then - echo "Installing dependencies" - ./bin/installdependencies.sh -fi -echo "Set file ownership of action runner" -chown -R "$user_name":"$user_name" . \ No newline at end of file +## 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/github_agent.linux.pkr.hcl b/images/linux-amzn2/github_agent.linux.pkr.hcl similarity index 70% rename from images/github_agent.linux.pkr.hcl rename to images/linux-amzn2/github_agent.linux.pkr.hcl index 94ddb68b07..9a460756fa 100644 --- a/images/github_agent.linux.pkr.hcl +++ b/images/linux-amzn2/github_agent.linux.pkr.hcl @@ -13,10 +13,16 @@ variable "action_runner_url" { default = "https://github.com/actions/runner/releases/download/v2.284.0/actions-runner-linux-x64-2.284.0.tar.gz" } +variable "region" { + description = "The region to build the image in" + type = string + default = "eu-west-1" +} + source "amazon-ebs" "githubrunner" { ami_name = "github-runner-amzn2-${formatdate("YYYYMMDDhhmm", timestamp())}" instance_type = "m3.medium" - region = "eu-west-1" + region = var.region source_ami_filter { filters = { name = "amzn2-ami-hvm-2.*-x86_64-ebs" @@ -54,18 +60,25 @@ build { environment_vars = [ "RUNNER_TARBALL_URL=${var.action_runner_url}" ] - script = "./install-runner.sh" + inline = [templatefile("../install-runner.sh", { + install_runner = templatefile("../../modules/runners/templates/install-runner.sh", { + ARM_PATCH = "" + S3_LOCATION_RUNNER_DISTRIBUTION = "" + }) + })] } provisioner "file" { - source = "startup.sh" - destination = "/tmp/startup.sh" + content = templatefile("../start-runner.sh", { + start_runner = file("../../modules/runners/templates/start-runner.sh") + }) + destination = "/tmp/start-runner.sh" } provisioner "shell" { inline = [ - "sudo mv /tmp/startup.sh /var/lib/cloud/scripts/per-boot/startup.sh", - "sudo chmod +x /var/lib/cloud/scripts/per-boot/startup.sh", + "sudo mv /tmp/startup.sh /var/lib/cloud/scripts/per-boot/start-runner.sh", + "sudo chmod +x /var/lib/cloud/scripts/per-boot/start-runner.sh", ] } diff --git a/images/start-runner.sh b/images/start-runner.sh new file mode 100644 index 0000000000..7555e44225 --- /dev/null +++ b/images/start-runner.sh @@ -0,0 +1,9 @@ +#!/bin/bash -e +exec > >(tee /var/log/runner-startup.log | logger -t user-data -s 2>/dev/console) 2>&1 + +cd /home/ec2-user/actions-runner + +## 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. +${start_runner} \ No newline at end of file diff --git a/modules/runners/main.tf b/modules/runners/main.tf index d9954b259c..4c5b7bf40d 100644 --- a/modules/runners/main.tf +++ b/modules/runners/main.tf @@ -4,22 +4,30 @@ locals { "Name" = format("%s-action-runner", var.environment) }, { - "Environment" = format("%s", var.environment) + "ghr:environment" = format("%s", var.environment) }, { - "enable_cloudwatch_agent" = var.enable_cloudwatch_agent + "ghr:enable_cloudwatch" = var.enable_cloudwatch_agent + }, + { + "ghr:run_as" = var.runner_as_root ? "root" : "ec2-user" + }, + { + # TODO: Update this to allow for ephemeral runners + "ghr:agent_mode" = "persistent" }, var.tags, ) - name_sg = var.overrides["name_sg"] == "" ? local.tags["Name"] : var.overrides["name_sg"] - name_runner = var.overrides["name_runner"] == "" ? local.tags["Name"] : var.overrides["name_runner"] - role_path = var.role_path == null ? "/${var.environment}/" : var.role_path - instance_profile_path = var.instance_profile_path == null ? "/${var.environment}/" : var.instance_profile_path - lambda_zip = var.lambda_zip == null ? "${path.module}/lambdas/runners/runners.zip" : var.lambda_zip - userdata_template = var.userdata_template == null ? "${path.module}/templates/user-data.sh" : var.userdata_template - userdata_arm_patch = "${path.module}/templates/arm-runner-patch.tpl" - userdata_install_config_runner = "${path.module}/templates/install-config-runner.sh" + name_sg = var.overrides["name_sg"] == "" ? local.tags["Name"] : var.overrides["name_sg"] + name_runner = var.overrides["name_runner"] == "" ? local.tags["Name"] : var.overrides["name_runner"] + role_path = var.role_path == null ? "/${var.environment}/" : var.role_path + instance_profile_path = var.instance_profile_path == null ? "/${var.environment}/" : var.instance_profile_path + lambda_zip = var.lambda_zip == null ? "${path.module}/lambdas/runners/runners.zip" : var.lambda_zip + userdata_template = var.userdata_template == null ? "${path.module}/templates/user-data.sh" : var.userdata_template + userdata_arm_patch = "${path.module}/templates/arm-runner-patch.tpl" + userdata_install_runner = "${path.module}/templates/install-runner.sh" + userdata_start_runner = "${path.module}/templates/start-runner.sh" instance_types = distinct(var.instance_types == null ? [var.instance_type] : var.instance_types) @@ -116,16 +124,19 @@ resource "aws_launch_template" "runner" { user_data = base64encode(templatefile(local.userdata_template, { - environment = var.environment - ## TODO I think we can still provide this tailoring functionality in this way but should make it more generic - pre_install = var.userdata_pre_install - post_install = var.userdata_post_install - enable_cloudwatch_agent = var.enable_cloudwatch_agent - # TODO Figure out if we need these here. We can pass them via Tags to the instance so we dont have to do trickey with env + pre_install = var.userdata_pre_install + install_runner = templatefile(local.userdata_install_runner, { + S3_LOCATION_RUNNER_DISTRIBUTION = var.s3_location_runner_binaries + ARM_PATCH = var.runner_architecture == "arm64" ? templatefile(local.userdata_arm_patch, {}) : "" + }) + post_install = var.userdata_post_install + start_runner = templatefile(local.userdata_start_runner, {}) + ghes_url = var.ghes_url + ghes_ssl_verify = var.ghes_ssl_verify + ## retain these for backwards compatibility + environment = var.environment + enable_cloudwatch_agent = var.enable_cloudwatch_agent ssm_key_cloudwatch_agent_config = var.enable_cloudwatch_agent ? aws_ssm_parameter.cloudwatch_agent_config_runner[0].name : "" - ghes_url = var.ghes_url - ghes_ssl_verify = var.ghes_ssl_verify - install_config_runner = local.install_config_runner })) tags = local.tags @@ -133,16 +144,6 @@ resource "aws_launch_template" "runner" { update_default_version = true } -locals { - arm_patch = var.runner_architecture == "arm64" ? templatefile(local.userdata_arm_patch, {}) : "" - install_config_runner = templatefile(local.userdata_install_config_runner, { - environment = var.environment - s3_location_runner_distribution = var.s3_location_runner_binaries - run_as_root_user = var.runner_as_root ? "root" : "" - arm_patch = local.arm_patch - }) -} - resource "aws_security_group" "runner_sg" { name_prefix = "${var.environment}-github-actions-runner-sg" description = "Github Actions Runner security group" diff --git a/modules/runners/templates/install-config-runner.sh b/modules/runners/templates/install-config-runner.sh deleted file mode 100644 index a1147303b1..0000000000 --- a/modules/runners/templates/install-config-runner.sh +++ /dev/null @@ -1,35 +0,0 @@ -cd /home/$USER_NAME -mkdir actions-runner && cd actions-runner - -TOKEN=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") -REGION=$(curl -f -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) - -aws s3 cp ${s3_location_runner_distribution} actions-runner.tar.gz --region $REGION -tar xzf ./actions-runner.tar.gz -rm -rf actions-runner.tar.gz - -${arm_patch} - -INSTANCE_ID=$(curl -f -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/instance-id) - -echo wait for configuration -while [[ $(aws ssm get-parameters --names ${environment}-$INSTANCE_ID --with-decryption --region $REGION | jq -r ".Parameters | .[0] | .Value") == null ]]; do - echo Waiting for configuration ... - sleep 1 -done -CONFIG=$(aws ssm get-parameters --names ${environment}-$INSTANCE_ID --with-decryption --region $REGION | jq -r ".Parameters | .[0] | .Value") -aws ssm delete-parameter --name ${environment}-$INSTANCE_ID --region $REGION - -export RUNNER_ALLOW_RUNASROOT=1 -os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release) -if [[ "$os_id" =~ ^ubuntu.* ]]; then - ./bin/installdependencies.sh -fi - -./config.sh --unattended --name $INSTANCE_ID --work "_work" $CONFIG - -chown -R $USER_NAME:$USER_NAME . -OVERWRITE_SERVICE_USER=${run_as_root_user} -SERVICE_USER=$${OVERWRITE_SERVICE_USER:-$USER_NAME} - -./svc.sh install $SERVICE_USER diff --git a/modules/runners/templates/install-runner.sh b/modules/runners/templates/install-runner.sh new file mode 100644 index 0000000000..ed5ab7b87f --- /dev/null +++ b/modules/runners/templates/install-runner.sh @@ -0,0 +1,48 @@ +## install the runner + +s3_location=${S3_LOCATION_RUNNER_DISTRIBUTION} + +if [ -z "$RUNNER_TARBALL_URL" ] && [ -z "$s3_location" ]; then + echo "Neither RUNNER_TARBALL_URL or s3_location are set" + exit 1 +fi + +file_name="actions-runner.tar.gz" + +echo "Creating actions-runner directory for the GH Action installtion" +cd /home/"$user_name" +mkdir actions-runner && cd actions-runner + + +if [[ -n "$RUNNER_TARBALL_URL" ]]; then + echo "Downloading the GH Action runner from $RUNNER_TARBALL_URL to $file_name" + curl -o $file_name -L "$RUNNER_TARBALL_URL" +else + echo "Retrieving TOKEN from AWS API" + token=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") + + region=$(curl -f -H "X-aws-ec2-metadata-token: $token" -v http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) + echo "Reteieved REGION from AWS API ($region)" + + echo "Downloading the GH Action runner from s3 bucket $s3_location" + aws s3 cp "$s3_location" "$file_name" --region "$region" +fi + +echo "Un-tar action runner" +tar xzf ./$file_name +echo "Delete tar file" +rm -rf $file_name + +${ARM_PATCH} + +echo "export RUNNER_ALLOW_RUNASROOT=1" +export RUNNER_ALLOW_RUNASROOT=1 + +os_id=$(awk -F= '/^ID/{print $2}' /etc/os-release) +if [[ "$os_id" =~ ^ubuntu.* ]]; then + echo "Installing dependencies" + ./bin/installdependencies.sh +fi + +echo "Set file ownership of action runner" +chown -R "$user_name":"$user_name" . \ No newline at end of file diff --git a/images/startup.sh b/modules/runners/templates/start-runner.sh similarity index 57% rename from images/startup.sh rename to modules/runners/templates/start-runner.sh index 2513290fbe..952d7707bb 100644 --- a/images/startup.sh +++ b/modules/runners/templates/start-runner.sh @@ -1,9 +1,4 @@ -#!/bin/bash -e -exec > >(tee /var/log/runner-startup.log | logger -t user-data -s 2>/dev/console) 2>&1 - -user_name=ec2-user - -cd /home/$user_name/actions-runner +## Retrieve instance metadata echo "Retrieving TOKEN from AWS API" token=$(curl -f -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 180") @@ -17,17 +12,26 @@ echo "Reteieved INSTANCE_ID from AWS API ($instance_id)" tags=$(aws ec2 describe-tags --region "$region" --filters "Name=resource-id,Values=$instance_id") echo "Retrieved tags from AWS API ($tags)" -environment=$(echo "$tags" | jq '.Tags[] | select(.Key == "Environment") | .Value') -echo "Reteieved environment tag - ($environment)" +environment=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:environment") | .Value') +echo "Reteieved ghr:environment tag - ($environment)" + +enable_cloudwatch_agent=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:enable_cloudwatch") | .Value') +echo "Reteieved ghr:enable_cloudwatch tag - ($enable_cloudwatch_agent)" + +run_as=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:_run_as") | .Value') +echo "Reteieved ghr:run_as tag - ($run_as)" -enable_cloudwatch_agent=$(echo "$tags" | jq '.Tags[] | select(.Key == "enable_cloudwatch_agent") | .Value') -echo "Reteieved enable_cloudwatch_agent tag - ($enable_cloudwatch_agent)" +agent_mode=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:agent_mode") | .Value') +echo "Reteieved ghr:agent_mode tag - ($agent_mode)" if [[ -n "$enable_cloudwatch_agent" ]]; then echo "Cloudwatch is enabled" amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:"$environment-cloudwatch_agent_config_runner" fi + +## Configure the runner + echo "Get GH Runner token from AWS SSM" config=$(aws ssm get-parameters --names "$environment"-"$instance_id" --with-decryption --region "$region" | jq -r ".Parameters | .[0] | .Value") @@ -43,13 +47,24 @@ aws ssm delete-parameter --name "$environment"-"$instance_id" --region "$region" echo "Configure GH Runner" ./config.sh --unattended --name "$instance_id" --work "_work" "$config" -# TODO this should also be passed via tags -service_user=${RUN_AGENT_AS_USER:-$user_name} -echo "Start the runner as user $service_user" -sudo -u "$service_user" -- ./run.sh -echo "Runner has finished" +## Start the runner + +service_user=${run_as:-ec2-user} +echo "Starting the runner as user $service_user" + +if [[ $agent_mode = "ephemeral" ]]; then + echo "Starting the runner in ephemeral mode" + sudo -u "$service_user" -- ./run.sh + echo "Runner has finished" -#service awslogsd stop -echo "Terminating instance" -aws ec2 terminate-instances --instance-ids "$instance_id" --region "$region" + #TODO is this line needed? + #service awslogsd stop + echo "Terminating instance" + aws ec2 terminate-instances --instance-ids "$instance_id" --region "$region" +else + echp "Installing the runner as a service" + ./svc.sh install "$service_user" + echo "Starting the runner in persistent mode" + ./svc.sh start +fi \ No newline at end of file diff --git a/modules/runners/templates/user-data.sh b/modules/runners/templates/user-data.sh index 0fdf4faa7b..8287e63aa2 100644 --- a/modules/runners/templates/user-data.sh +++ b/modules/runners/templates/user-data.sh @@ -5,11 +5,6 @@ ${pre_install} yum update -y -%{ if enable_cloudwatch_agent ~} -yum install amazon-cloudwatch-agent -y -amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:${ssm_key_cloudwatch_agent_config} -%{ endif ~} - # Install docker amazon-linux-extras install docker service docker start @@ -17,9 +12,10 @@ usermod -a -G docker ec2-user yum install -y curl jq git -USER_NAME=ec2-user -${install_config_runner} +user_name=ec2-user + +${install_runner} ${post_install} -./svc.sh start +${start_runner} From f8ad58c1c194228654af90beef5766eaa2638f09 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Mon, 29 Nov 2021 20:09:19 +0100 Subject: [PATCH 10/36] Fix interpolation issues in template file --- modules/runners/templates/start-runner.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/modules/runners/templates/start-runner.sh b/modules/runners/templates/start-runner.sh index 952d7707bb..7366bcadf3 100644 --- a/modules/runners/templates/start-runner.sh +++ b/modules/runners/templates/start-runner.sh @@ -50,12 +50,15 @@ echo "Configure GH Runner" ## Start the runner -service_user=${run_as:-ec2-user} -echo "Starting the runner as user $service_user" + +if [ -z "$run_as" ]; then + run_as="ec2-user" +fi +echo "Starting the runner as user $run_as" if [[ $agent_mode = "ephemeral" ]]; then echo "Starting the runner in ephemeral mode" - sudo -u "$service_user" -- ./run.sh + sudo -u "$run_as" -- ./run.sh echo "Runner has finished" #TODO is this line needed? @@ -64,7 +67,7 @@ if [[ $agent_mode = "ephemeral" ]]; then aws ec2 terminate-instances --instance-ids "$instance_id" --region "$region" else echp "Installing the runner as a service" - ./svc.sh install "$service_user" + ./svc.sh install "$run_as" echo "Starting the runner in persistent mode" ./svc.sh start fi \ No newline at end of file From b3d2fcf86a0899c9aed67058a5b7a52d5e7e8f0c Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Mon, 29 Nov 2021 20:12:45 +0100 Subject: [PATCH 11/36] fix build --- .github/workflows/packer-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index d294facd34..3925e4a7b4 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -20,7 +20,7 @@ jobs: image: hashicorp/packer:1.7.8 defaults: run: - working-directory: images/ + working-directory: images/linux-amzn2 steps: - name: "Checkout" uses: actions/checkout@v2 From 9e0612ec963553a34da529a7b2d09bb617485c61 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Mon, 29 Nov 2021 20:14:05 +0100 Subject: [PATCH 12/36] Fix formatting --- images/linux-amzn2/github_agent.linux.pkr.hcl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/images/linux-amzn2/github_agent.linux.pkr.hcl b/images/linux-amzn2/github_agent.linux.pkr.hcl index 9a460756fa..7920bd944c 100644 --- a/images/linux-amzn2/github_agent.linux.pkr.hcl +++ b/images/linux-amzn2/github_agent.linux.pkr.hcl @@ -62,14 +62,14 @@ build { ] inline = [templatefile("../install-runner.sh", { install_runner = templatefile("../../modules/runners/templates/install-runner.sh", { - ARM_PATCH = "" + ARM_PATCH = "" S3_LOCATION_RUNNER_DISTRIBUTION = "" }) })] } provisioner "file" { - content = templatefile("../start-runner.sh", { + content = templatefile("../start-runner.sh", { start_runner = file("../../modules/runners/templates/start-runner.sh") }) destination = "/tmp/start-runner.sh" From 8233ca9ff850310e973a6233d839aefaec6062aa Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Mon, 29 Nov 2021 20:30:57 +0100 Subject: [PATCH 13/36] minor tweaks and fixes --- images/install-runner.sh | 1 - images/linux-amzn2/github_agent.linux.pkr.hcl | 2 +- modules/runners/templates/install-runner.sh | 2 ++ modules/runners/templates/start-runner.sh | 2 ++ 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/images/install-runner.sh b/images/install-runner.sh index 6a043a2c79..e042333f00 100644 --- a/images/install-runner.sh +++ b/images/install-runner.sh @@ -1,5 +1,4 @@ #!/bin/bash -e -set -e user_name=ec2-user diff --git a/images/linux-amzn2/github_agent.linux.pkr.hcl b/images/linux-amzn2/github_agent.linux.pkr.hcl index 7920bd944c..f7a5114186 100644 --- a/images/linux-amzn2/github_agent.linux.pkr.hcl +++ b/images/linux-amzn2/github_agent.linux.pkr.hcl @@ -77,7 +77,7 @@ build { provisioner "shell" { inline = [ - "sudo mv /tmp/startup.sh /var/lib/cloud/scripts/per-boot/start-runner.sh", + "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", ] } diff --git a/modules/runners/templates/install-runner.sh b/modules/runners/templates/install-runner.sh index ed5ab7b87f..94e440e265 100644 --- a/modules/runners/templates/install-runner.sh +++ b/modules/runners/templates/install-runner.sh @@ -1,3 +1,5 @@ +# shellcheck shell=bash + ## install the runner s3_location=${S3_LOCATION_RUNNER_DISTRIBUTION} diff --git a/modules/runners/templates/start-runner.sh b/modules/runners/templates/start-runner.sh index 7366bcadf3..7f98ef4264 100644 --- a/modules/runners/templates/start-runner.sh +++ b/modules/runners/templates/start-runner.sh @@ -1,3 +1,5 @@ +# shellcheck shell=bash + ## Retrieve instance metadata echo "Retrieving TOKEN from AWS API" From 41446346a290ca54325a9066efec59cdee9e741e Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Tue, 30 Nov 2021 16:52:44 +0100 Subject: [PATCH 14/36] Fixes from testing --- modules/runners/logging.tf | 2 +- modules/runners/templates/start-runner.sh | 28 +++++++++++------------ modules/runners/templates/user-data.sh | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/modules/runners/logging.tf b/modules/runners/logging.tf index b66fe29c33..e0b19d59a8 100644 --- a/modules/runners/logging.tf +++ b/modules/runners/logging.tf @@ -28,7 +28,7 @@ resource "aws_cloudwatch_log_group" "gh_runners" { } resource "aws_iam_role_policy" "cloudwatch" { - count = var.enable_ssm_on_runners ? 1 : 0 + count = var.enable_cloudwatch_agent ? 1 : 0 name = "CloudWatchLogginAndMetrics" role = aws_iam_role.runner.name policy = templatefile("${path.module}/policies/instance-cloudwatch-policy.json", diff --git a/modules/runners/templates/start-runner.sh b/modules/runners/templates/start-runner.sh index 7f98ef4264..9babc9ee9f 100644 --- a/modules/runners/templates/start-runner.sh +++ b/modules/runners/templates/start-runner.sh @@ -14,21 +14,21 @@ echo "Reteieved INSTANCE_ID from AWS API ($instance_id)" tags=$(aws ec2 describe-tags --region "$region" --filters "Name=resource-id,Values=$instance_id") echo "Retrieved tags from AWS API ($tags)" -environment=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:environment") | .Value') +environment=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:environment") | .Value') echo "Reteieved ghr:environment tag - ($environment)" -enable_cloudwatch_agent=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:enable_cloudwatch") | .Value') +enable_cloudwatch_agent=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:enable_cloudwatch") | .Value') echo "Reteieved ghr:enable_cloudwatch tag - ($enable_cloudwatch_agent)" -run_as=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:_run_as") | .Value') +run_as=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:_run_as") | .Value') echo "Reteieved ghr:run_as tag - ($run_as)" -agent_mode=$(echo "$tags" | jq '.Tags[] | select(.Key == "ghr:agent_mode") | .Value') +agent_mode=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:agent_mode") | .Value') echo "Reteieved ghr:agent_mode tag - ($agent_mode)" if [[ -n "$enable_cloudwatch_agent" ]]; then echo "Cloudwatch is enabled" - amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c ssm:"$environment-cloudwatch_agent_config_runner" + amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c "ssm:$environment-cloudwatch_agent_config_runner" fi @@ -46,16 +46,15 @@ done echo "Delete GH Runner token from AWS SSM" aws ssm delete-parameter --name "$environment"-"$instance_id" --region "$region" -echo "Configure GH Runner" -./config.sh --unattended --name "$instance_id" --work "_work" "$config" - - -## Start the runner - - if [ -z "$run_as" ]; then run_as="ec2-user" fi + +echo "Configure GH Runner as user $run_as" +sudo -u "$run_as" -- ./config.sh --unattended --name "$instance_id" --work "_work" $${config} + + +## Start the runner echo "Starting the runner as user $run_as" if [[ $agent_mode = "ephemeral" ]]; then @@ -64,11 +63,12 @@ if [[ $agent_mode = "ephemeral" ]]; then echo "Runner has finished" #TODO is this line needed? - #service awslogsd stop + echo "Stopping cloudwatch service" + service awslogsd stop echo "Terminating instance" aws ec2 terminate-instances --instance-ids "$instance_id" --region "$region" else - echp "Installing the runner as a service" + echo "Installing the runner as a service" ./svc.sh install "$run_as" echo "Starting the runner in persistent mode" ./svc.sh start diff --git a/modules/runners/templates/user-data.sh b/modules/runners/templates/user-data.sh index 8287e63aa2..568e48c8c6 100644 --- a/modules/runners/templates/user-data.sh +++ b/modules/runners/templates/user-data.sh @@ -10,7 +10,7 @@ amazon-linux-extras install docker service docker start usermod -a -G docker ec2-user -yum install -y curl jq git +yum install -y amazon-cloudwatch-agent curl jq git user_name=ec2-user From 80b1b902d48343174102bd0ce55b9b2ec24b1d4f Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Tue, 30 Nov 2021 17:32:29 +0100 Subject: [PATCH 15/36] Enable docker on boot --- images/linux-amzn2/github_agent.linux.pkr.hcl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/images/linux-amzn2/github_agent.linux.pkr.hcl b/images/linux-amzn2/github_agent.linux.pkr.hcl index f7a5114186..b60d92fb04 100644 --- a/images/linux-amzn2/github_agent.linux.pkr.hcl +++ b/images/linux-amzn2/github_agent.linux.pkr.hcl @@ -51,6 +51,8 @@ build { "sudo yum update -y", "sudo yum install -y amazon-cloudwatch-agent curl jq git", "sudo amazon-linux-extras install docker", + "sudo systemctl enable docker.service", + "sudo systemctl enable containerd.service", "sudo service docker start", "sudo usermod -a -G docker ec2-user", ] @@ -70,7 +72,7 @@ build { provisioner "file" { content = templatefile("../start-runner.sh", { - start_runner = file("../../modules/runners/templates/start-runner.sh") + start_runner = templatefile("../../modules/runners/templates/start-runner.sh", {}) }) destination = "/tmp/start-runner.sh" } From 33e9105521a7521d405dca10f51d7abb84f9df35 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Tue, 30 Nov 2021 17:47:12 +0100 Subject: [PATCH 16/36] Add in output of start time for the runner --- modules/runners/templates/start-runner.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/modules/runners/templates/start-runner.sh b/modules/runners/templates/start-runner.sh index 9babc9ee9f..491fc5b647 100644 --- a/modules/runners/templates/start-runner.sh +++ b/modules/runners/templates/start-runner.sh @@ -53,16 +53,15 @@ fi echo "Configure GH Runner as user $run_as" sudo -u "$run_as" -- ./config.sh --unattended --name "$instance_id" --work "_work" $${config} - ## Start the runner +echo "Starting runner after $(awk '{print int($1/3600)":"int(($1%3600)/60)":"int($1%60)}' /proc/uptime)" echo "Starting the runner as user $run_as" if [[ $agent_mode = "ephemeral" ]]; then echo "Starting the runner in ephemeral mode" sudo -u "$run_as" -- ./run.sh echo "Runner has finished" - - #TODO is this line needed? + echo "Stopping cloudwatch service" service awslogsd stop echo "Terminating instance" From af9198fa363fa364589f833a84f8d8a1d58a4fdd Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Tue, 30 Nov 2021 18:03:26 +0100 Subject: [PATCH 17/36] Scoop up the runner log --- modules/runners/variables.tf | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index fd72a55710..dd539e7efa 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -292,6 +292,12 @@ variable "runner_log_files" { "file_path" : "/var/log/user-data.log", "log_stream_name" : "{instance_id}" }, + { + "log_group_name" : "runner-startup", + "prefix_log_group" : true, + "file_path" : "/var/log/runner-startup.log", + "log_stream_name" : "{instance_id}" + }, { "log_group_name" : "runner", "prefix_log_group" : true, From 08a0a6680ae323af585c37fadd0e71e1e2ee3715 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Tue, 30 Nov 2021 18:05:57 +0100 Subject: [PATCH 18/36] Add a powershell build script for windows users --- .ci/build.ps1 | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .ci/build.ps1 diff --git a/.ci/build.ps1 b/.ci/build.ps1 new file mode 100644 index 0000000000..d92a1b62e6 --- /dev/null +++ b/.ci/build.ps1 @@ -0,0 +1,8 @@ +$TOP_DIR=$(git rev-parse --show-toplevel) +$OUTPUT_DIR="$TOP_DIR/lambda_output" + +New-Item "$OUTPUT_DIR" -ItemType Directory -ErrorAction SilentlyContinue + +$env:DOCKER_BUILDKIT=1 +docker build --no-cache --target=final --output=type=local,dest="$OUTPUT_DIR" -f "$TOP_DIR/.ci/Dockerfile" "$TOP_DIR" + From 925ffb9ae0731160f3ddba8e408d0dd8f958b6aa Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Tue, 30 Nov 2021 18:18:22 +0100 Subject: [PATCH 19/36] Fix formatting --- images/linux-amzn2/github_agent.linux.pkr.hcl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/images/linux-amzn2/github_agent.linux.pkr.hcl b/images/linux-amzn2/github_agent.linux.pkr.hcl index b60d92fb04..57f42f777c 100644 --- a/images/linux-amzn2/github_agent.linux.pkr.hcl +++ b/images/linux-amzn2/github_agent.linux.pkr.hcl @@ -52,7 +52,7 @@ build { "sudo yum install -y amazon-cloudwatch-agent curl jq git", "sudo amazon-linux-extras install docker", "sudo systemctl enable docker.service", - "sudo systemctl enable containerd.service", + "sudo systemctl enable containerd.service", "sudo service docker start", "sudo usermod -a -G docker ec2-user", ] From 11f5a2c0b12b7cd833da78f6b2b5475726a6c9a1 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Wed, 1 Dec 2021 11:15:59 +0100 Subject: [PATCH 20/36] Use SSM parameters for configuration Its best practice to use SSM parameters for configuration of the runners. In adding this i have also added parameter path based config so its easy to extend in the future. --- main.tf | 3 ++- modules/runners/main.tf | 13 ------------ modules/runners/policies-runner.tf | 5 ++++- .../instance-ssm-parameters-policy.json | 7 ++++--- modules/runners/runner-config.tf | 21 +++++++++++++++++++ modules/runners/templates/start-runner.sh | 15 +++++++------ 6 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 modules/runners/runner-config.tf diff --git a/main.tf b/main.tf index 4c9b3858ec..169e94ede8 100644 --- a/main.tf +++ b/main.tf @@ -1,6 +1,7 @@ locals { tags = merge(var.tags, { - Environment = var.environment + Environment = var.environment, + "ghr:environment" = format("%s", var.environment) }) s3_action_runner_url = "s3://${module.runner_binaries.bucket.id}/${module.runner_binaries.runner_distribution_object_key}" diff --git a/modules/runners/main.tf b/modules/runners/main.tf index 4c5b7bf40d..b1480124f1 100644 --- a/modules/runners/main.tf +++ b/modules/runners/main.tf @@ -3,19 +3,6 @@ locals { { "Name" = format("%s-action-runner", var.environment) }, - { - "ghr:environment" = format("%s", var.environment) - }, - { - "ghr:enable_cloudwatch" = var.enable_cloudwatch_agent - }, - { - "ghr:run_as" = var.runner_as_root ? "root" : "ec2-user" - }, - { - # TODO: Update this to allow for ephemeral runners - "ghr:agent_mode" = "persistent" - }, var.tags, ) diff --git a/modules/runners/policies-runner.tf b/modules/runners/policies-runner.tf index 3d649babff..7efae1326a 100644 --- a/modules/runners/policies-runner.tf +++ b/modules/runners/policies-runner.tf @@ -26,7 +26,10 @@ resource "aws_iam_role_policy" "ssm_parameters" { role = aws_iam_role.runner.name policy = templatefile("${path.module}/policies/instance-ssm-parameters-policy.json", { - arn_ssm_parameters = "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}-*" + arn_ssm_parameters = jsonencode([ + "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}-*", + "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}/*" + ]) } ) } diff --git a/modules/runners/policies/instance-ssm-parameters-policy.json b/modules/runners/policies/instance-ssm-parameters-policy.json index 96e8b02c95..2990e93bd6 100644 --- a/modules/runners/policies/instance-ssm-parameters-policy.json +++ b/modules/runners/policies/instance-ssm-parameters-policy.json @@ -6,15 +6,16 @@ "Action": [ "ssm:DeleteParameter" ], - "Resource": "${arn_ssm_parameters}" + "Resource": ${arn_ssm_parameters} }, { "Effect": "Allow", "Action": [ + "ssm:GetParameter", "ssm:GetParameters", - "ssm:GetParameter" + "ssm:GetParametersByPath" ], - "Resource": "${arn_ssm_parameters}" + "Resource": ${arn_ssm_parameters} } ] } diff --git a/modules/runners/runner-config.tf b/modules/runners/runner-config.tf new file mode 100644 index 0000000000..eb6370e58f --- /dev/null +++ b/modules/runners/runner-config.tf @@ -0,0 +1,21 @@ +resource "aws_ssm_parameter" "runner_config_run_as" { + name = "/${var.environment}/runner/run-as" + type = "String" + value = var.runner_as_root ? "root" : "ec2-user" + tags = local.tags +} + +resource "aws_ssm_parameter" "runner_agent_mode" { + name = "/${var.environment}/runner/agent-mode" + type = "String" + # TODO: Update this to allow for ephemeral runners + value = "persistent" + tags = local.tags +} + +resource "aws_ssm_parameter" "runner_enable_cloudwatch" { + name = "/${var.environment}/runner/enable-cloudwatch" + type = "String" + value = var.enable_cloudwatch_agent + tags = local.tags +} diff --git a/modules/runners/templates/start-runner.sh b/modules/runners/templates/start-runner.sh index 491fc5b647..92e1e34526 100644 --- a/modules/runners/templates/start-runner.sh +++ b/modules/runners/templates/start-runner.sh @@ -17,14 +17,17 @@ echo "Retrieved tags from AWS API ($tags)" environment=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:environment") | .Value') echo "Reteieved ghr:environment tag - ($environment)" -enable_cloudwatch_agent=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:enable_cloudwatch") | .Value') -echo "Reteieved ghr:enable_cloudwatch tag - ($enable_cloudwatch_agent)" +parameters=$(aws ssm get-parameters-by-path --path "/$environment/runner" --region "$region" --query "Parameters[*].{Name:Name,Value:Value}") +echo "Retrieved parameters from AWS SSM ($parameters)" -run_as=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:_run_as") | .Value') -echo "Reteieved ghr:run_as tag - ($run_as)" +run_as=$(echo "$parameters" | jq --arg environment "$environment" -r '.[] | select(.Name == "/\($environment)/runner/run-as") | .Value') +echo "Retrieved /$environment/runner/run-as parameter - ($run_as)" -agent_mode=$(echo "$tags" | jq -r '.Tags[] | select(.Key == "ghr:agent_mode") | .Value') -echo "Reteieved ghr:agent_mode tag - ($agent_mode)" +enable_cloudwatch_agent=$(echo "$parameters" | jq --arg environment "$environment" -r '.[] | select(.Name == "/\($environment)/runner/enable-cloudwatch") | .Value') +echo "Retrieved /$environment/runner/enable-cloudwatch parameter - ($enable_cloudwatch_agent)" + +agent_mode=$(echo "$parameters" | jq --arg environment "$environment" -r '.[] | select(.Name == "/\($environment)/runner/agent-mode") | .Value') +echo "Retrieved /$environment/runner/agent-mode parameter - ($agent_mode)" if [[ -n "$enable_cloudwatch_agent" ]]; then echo "Cloudwatch is enabled" From 72e77dcf29ae3fe5937121f2727fdbf8b139d865 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Wed, 1 Dec 2021 11:38:08 +0100 Subject: [PATCH 21/36] Make the SSM policy more specific --- modules/runners/policies-runner.tf | 6 ++---- .../runners/policies/instance-ssm-parameters-policy.json | 7 +++++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/runners/policies-runner.tf b/modules/runners/policies-runner.tf index 7efae1326a..b94959f370 100644 --- a/modules/runners/policies-runner.tf +++ b/modules/runners/policies-runner.tf @@ -26,10 +26,8 @@ resource "aws_iam_role_policy" "ssm_parameters" { role = aws_iam_role.runner.name policy = templatefile("${path.module}/policies/instance-ssm-parameters-policy.json", { - arn_ssm_parameters = jsonencode([ - "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}-*", - "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}/*" - ]) + arn_ssm_parameters_prefix= "arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}-*" + arn_ssm_parameters_path ="arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.environment}/*" } ) } diff --git a/modules/runners/policies/instance-ssm-parameters-policy.json b/modules/runners/policies/instance-ssm-parameters-policy.json index 2990e93bd6..5a7aa9e356 100644 --- a/modules/runners/policies/instance-ssm-parameters-policy.json +++ b/modules/runners/policies/instance-ssm-parameters-policy.json @@ -6,7 +6,7 @@ "Action": [ "ssm:DeleteParameter" ], - "Resource": ${arn_ssm_parameters} + "Resource": "${arn_ssm_parameters_prefix}" }, { "Effect": "Allow", @@ -15,7 +15,10 @@ "ssm:GetParameters", "ssm:GetParametersByPath" ], - "Resource": ${arn_ssm_parameters} + "Resource": [ + "${arn_ssm_parameters_prefix}", + "${arn_ssm_parameters_path}" + ] } ] } From 632de437186e8afce7b9c80e02132d775c2420e7 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 13:48:04 +0100 Subject: [PATCH 22/36] Update .github/workflows/packer-build.yml Co-authored-by: Niek Palm --- .github/workflows/packer-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/packer-build.yml b/.github/workflows/packer-build.yml index 3925e4a7b4..96f3de47a3 100644 --- a/.github/workflows/packer-build.yml +++ b/.github/workflows/packer-build.yml @@ -14,7 +14,7 @@ env: jobs: verify_packer: - name: Verify image + name: Verify packer runs-on: ubuntu-latest container: image: hashicorp/packer:1.7.8 From 318bdde2b19bfd4ab7cdffa138b3dc59cf945d31 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 14:27:29 +0100 Subject: [PATCH 23/36] Added condition to the describe tags policy --- .../runners/policies/instance-describe-tags-policy.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/runners/policies/instance-describe-tags-policy.json b/modules/runners/policies/instance-describe-tags-policy.json index 7f93d36246..59c34fcf26 100644 --- a/modules/runners/policies/instance-describe-tags-policy.json +++ b/modules/runners/policies/instance-describe-tags-policy.json @@ -4,7 +4,12 @@ { "Effect": "Allow", "Action": "ec2:DescribeTags", - "Resource": "*" + "Resource": "*", + "Condition": { + "ArnEquals": { + "aws:SourceArn": "${ec2:SourceInstanceARN}" + } + } } ] } \ No newline at end of file From eb1910f31d2cea44d4e1197dbbe6748f071c3fb3 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 14:38:22 +0100 Subject: [PATCH 24/36] Dont use templatefile on the tags policy Because of the use of ${} in the policy terraform is trying to replace it. --- modules/runners/policies-runner.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/runners/policies-runner.tf b/modules/runners/policies-runner.tf index b94959f370..212f9a4b24 100644 --- a/modules/runners/policies-runner.tf +++ b/modules/runners/policies-runner.tf @@ -45,7 +45,7 @@ resource "aws_iam_role_policy" "dist_bucket" { resource "aws_iam_role_policy" "describe_tags" { name = "runner-describe-tags" role = aws_iam_role.runner.name - policy = templatefile("${path.module}/policies/instance-describe-tags-policy.json", {}) + policy = file("${path.module}/policies/instance-describe-tags-policy.json") } resource "aws_iam_role_policy_attachment" "managed_policies" { From 82db546a5766dd99a37d3790825d19e9601e238d Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 15:24:05 +0100 Subject: [PATCH 25/36] Added an option to turn off userdata scripting --- main.tf | 1 + modules/runners/main.tf | 4 ++-- modules/runners/variables.tf | 6 ++++++ variables.tf | 6 ++++++ 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/main.tf b/main.tf index 169e94ede8..da49358bbb 100644 --- a/main.tf +++ b/main.tf @@ -126,6 +126,7 @@ module "runners" { role_path = var.role_path role_permissions_boundary = var.role_permissions_boundary + userdata_enabled = var.userdata_enabled userdata_template = var.userdata_template userdata_pre_install = var.userdata_pre_install userdata_post_install = var.userdata_post_install diff --git a/modules/runners/main.tf b/modules/runners/main.tf index b1480124f1..22d432fb88 100644 --- a/modules/runners/main.tf +++ b/modules/runners/main.tf @@ -110,7 +110,7 @@ resource "aws_launch_template" "runner" { } - user_data = base64encode(templatefile(local.userdata_template, { + user_data = var.userdata_enabled ? base64encode(templatefile(local.userdata_template, { pre_install = var.userdata_pre_install install_runner = templatefile(local.userdata_install_runner, { S3_LOCATION_RUNNER_DISTRIBUTION = var.s3_location_runner_binaries @@ -124,7 +124,7 @@ resource "aws_launch_template" "runner" { environment = var.environment enable_cloudwatch_agent = var.enable_cloudwatch_agent ssm_key_cloudwatch_agent_config = var.enable_cloudwatch_agent ? aws_ssm_parameter.cloudwatch_agent_config_runner[0].name : "" - })) + })) : "" tags = local.tags diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index dd539e7efa..643050b287 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -84,6 +84,12 @@ variable "ami_owners" { default = ["amazon"] } +variable "userdata_enabled" { + description = "Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI" + type = bool + default = true +} + variable "userdata_template" { description = "Alternative user-data template, replacing the default template. By providing your own user_data you have to take care of installing all required software, including the action runner. Variables userdata_pre/post_install are ignored." type = string diff --git a/variables.tf b/variables.tf index 21b8ed3dea..2f171057ba 100644 --- a/variables.tf +++ b/variables.tf @@ -165,6 +165,12 @@ variable "kms_key_arn" { default = null } +variable "userdata_enabled" { + description = "Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI" + type = bool + default = true +} + variable "userdata_template" { description = "Alternative user-data template, replacing the default template. By providing your own user_data you have to take care of installing all required software, including the action runner. Variables userdata_pre/post_install are ignored." type = string From 0f8f1144286c314fa3ddcda82cc0b0fc5da810ec Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 15:25:53 +0100 Subject: [PATCH 26/36] Added/updated documentation --- README.md | 9 +++++---- images/README.md | 37 +++++++++++++++++++++++++++++++++++++ modules/runners/README.md | 7 ++++++- 3 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 images/README.md diff --git a/README.md b/README.md index 58aef2a7cb..f8cc5e3286 100644 --- a/README.md +++ b/README.md @@ -346,10 +346,10 @@ In case the setup does not work as intended follow the trace of events: | Name | Source | Version | |------|--------|---------| -| [runner\_binaries](#module\_runner\_binaries) | ./modules/runner-binaries-syncer | n/a | -| [runners](#module\_runners) | ./modules/runners | n/a | -| [ssm](#module\_ssm) | ./modules/ssm | n/a | -| [webhook](#module\_webhook) | ./modules/webhook | n/a | +| [runner\_binaries](#module\_runner\_binaries) | ./modules/runner-binaries-syncer | | +| [runners](#module\_runners) | ./modules/runners | | +| [ssm](#module\_ssm) | ./modules/ssm | | +| [webhook](#module\_webhook) | ./modules/webhook | | ## Resources @@ -422,6 +422,7 @@ In case the setup does not work as intended follow the trace of events: | [syncer\_lambda\_s3\_key](#input\_syncer\_lambda\_s3\_key) | S3 key for syncer lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no | | [syncer\_lambda\_s3\_object\_version](#input\_syncer\_lambda\_s3\_object\_version) | S3 object version for syncer lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no | | [tags](#input\_tags) | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no | +| [userdata\_enabled](#input\_userdata\_enabled) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | | [userdata\_post\_install](#input\_userdata\_post\_install) | Script to be ran after the GitHub Actions runner is installed on the EC2 instances | `string` | `""` | no | | [userdata\_pre\_install](#input\_userdata\_pre\_install) | Script to be ran before the GitHub Actions runner is installed on the EC2 instances | `string` | `""` | no | | [userdata\_template](#input\_userdata\_template) | Alternative user-data template, replacing the default template. By providing your own user\_data you have to take care of installing all required software, including the action runner. Variables userdata\_pre/post\_install are ignored. | `string` | `null` | no | diff --git a/images/README.md b/images/README.md new file mode 100644 index 0000000000..cc0c6e70ab --- /dev/null +++ b/images/README.md @@ -0,0 +1,37 @@ +# Prebuilt Images + +The images inside this folder are pre-built images designed to shorten the boot time of your runners and make using ephemeral runners a faster experience. + +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. + +## Building your own + +To build these images you first need to install packer. +You will also need an amazon account and to have provisioned your credentials for packer to consume. + +Assuming you are building the `linux-amzn2` image. Then run the following from within the `linux-amzn2` folder + +```bash +packer init . +packer validate . +packer build github_agent.linux.pkr.hcl +``` + +Your image will then begin to build inside AWS and when finished you will be provided with complete AMI. + +## Using your image + +To use your image in the terraform modules you will need to set some values on the module. + +Assuming you have built the `linux-amzn2` image which has a pre-defined AMI name in the following format `github-runner-amzn2-YYYYMMDDhhmm` you can use the following values. + +```hcl +# set the name of the ami to use +ami_filter = { name = ["github-runner-amzn2-2021*"] } +# provide the owner id of +ami_owners = [""] + +userdata_enabled = false +``` diff --git a/modules/runners/README.md b/modules/runners/README.md index fcc9d6b85b..bf6fee8346 100644 --- a/modules/runners/README.md +++ b/modules/runners/README.md @@ -79,6 +79,7 @@ No modules. | [aws_iam_role.scale_down](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role.scale_up](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource | | [aws_iam_role_policy.cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | +| [aws_iam_role_policy.describe_tags](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy.dist_bucket](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy.runner_session_manager_aws_managed](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | | [aws_iam_role_policy.scale_down](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource | @@ -98,6 +99,9 @@ No modules. | [aws_launch_template.runner](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/launch_template) | resource | | [aws_security_group.runner_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource | | [aws_ssm_parameter.cloudwatch_agent_config_runner](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | +| [aws_ssm_parameter.runner_agent_mode](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | +| [aws_ssm_parameter.runner_config_run_as](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | +| [aws_ssm_parameter.runner_enable_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource | | [aws_ami.runner](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ami) | data source | | [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | | [aws_iam_policy_document.lambda_assume_role_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | @@ -149,7 +153,7 @@ No modules. | [runner\_extra\_labels](#input\_runner\_extra\_labels) | Extra labels for the runners (GitHub). Separate each label by a comma | `string` | `""` | no | | [runner\_group\_name](#input\_runner\_group\_name) | Name of the runner group. | `string` | `"Default"` | no | | [runner\_iam\_role\_managed\_policy\_arns](#input\_runner\_iam\_role\_managed\_policy\_arns) | Attach AWS or customer-managed IAM policies (by ARN) to the runner IAM role | `list(string)` | `[]` | no | -| [runner\_log\_files](#input\_runner\_log\_files) | (optional) List of logfiles to send to CloudWatch, will only be used if `enable_cloudwatch_agent` is set to true. Object description: `log_group_name`: Name of the log group, `prefix_log_group`: If true, the log group name will be prefixed with `/github-self-hosted-runners/`, `file_path`: path to the log file, `log_stream_name`: name of the log stream. |
list(object({
log_group_name = string
prefix_log_group = bool
file_path = string
log_stream_name = string
}))
|
[
{
"file_path": "/var/log/messages",
"log_group_name": "messages",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
},
{
"file_path": "/var/log/user-data.log",
"log_group_name": "user_data",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
},
{
"file_path": "/home/ec2-user/actions-runner/_diag/Runner_**.log",
"log_group_name": "runner",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
}
]
| no | +| [runner\_log\_files](#input\_runner\_log\_files) | (optional) List of logfiles to send to CloudWatch, will only be used if `enable_cloudwatch_agent` is set to true. Object description: `log_group_name`: Name of the log group, `prefix_log_group`: If true, the log group name will be prefixed with `/github-self-hosted-runners/`, `file_path`: path to the log file, `log_stream_name`: name of the log stream. |
list(object({
log_group_name = string
prefix_log_group = bool
file_path = string
log_stream_name = string
}))
|
[
{
"file_path": "/var/log/messages",
"log_group_name": "messages",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
},
{
"file_path": "/var/log/user-data.log",
"log_group_name": "user_data",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
},
{
"file_path": "/var/log/runner-startup.log",
"log_group_name": "runner-startup",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
},
{
"file_path": "/home/ec2-user/actions-runner/_diag/Runner_**.log",
"log_group_name": "runner",
"log_stream_name": "{instance_id}",
"prefix_log_group": true
}
]
| no | | [runners\_lambda\_s3\_key](#input\_runners\_lambda\_s3\_key) | S3 key for runners lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no | | [runners\_lambda\_s3\_object\_version](#input\_runners\_lambda\_s3\_object\_version) | S3 object version for runners lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no | | [runners\_maximum\_count](#input\_runners\_maximum\_count) | The maximum number of runners that will be created. | `number` | `3` | no | @@ -160,6 +164,7 @@ No modules. | [sqs\_build\_queue](#input\_sqs\_build\_queue) | SQS queue to consume accepted build events. |
object({
arn = string
})
| n/a | yes | | [subnet\_ids](#input\_subnet\_ids) | List of subnets in which the action runners will be launched, the subnets needs to be subnets in the `vpc_id`. | `list(string)` | n/a | yes | | [tags](#input\_tags) | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no | +| [userdata\_enabled](#input\_userdata\_enabled) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | | [userdata\_post\_install](#input\_userdata\_post\_install) | User-data script snippet to insert after GitHub action runner install | `string` | `""` | no | | [userdata\_pre\_install](#input\_userdata\_pre\_install) | User-data script snippet to insert before GitHub action runner install | `string` | `""` | no | | [userdata\_template](#input\_userdata\_template) | Alternative user-data template, replacing the default template. By providing your own user\_data you have to take care of installing all required software, including the action runner. Variables userdata\_pre/post\_install are ignored. | `string` | `null` | no | From eb15f6b96d5e51385790287b8c59a6eb4cd2adc3 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 16:24:56 +0100 Subject: [PATCH 27/36] Revert policy as it has no effect on the permissions --- .../runners/policies/instance-describe-tags-policy.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/runners/policies/instance-describe-tags-policy.json b/modules/runners/policies/instance-describe-tags-policy.json index 59c34fcf26..c66074fdaf 100644 --- a/modules/runners/policies/instance-describe-tags-policy.json +++ b/modules/runners/policies/instance-describe-tags-policy.json @@ -4,12 +4,7 @@ { "Effect": "Allow", "Action": "ec2:DescribeTags", - "Resource": "*", - "Condition": { - "ArnEquals": { - "aws:SourceArn": "${ec2:SourceInstanceARN}" - } - } + "Resource": "*" } ] } \ No newline at end of file From 280a680942e4c66396fc52361029c71e8408b3d5 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 16:25:11 +0100 Subject: [PATCH 28/36] Add reference to prebuilt images in the main readme --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f8cc5e3286..4e292f6ae1 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ This [Terraform](https://www.terraform.io/) module creates the required infrastr - [Install app](#install-app) - [Encryption](#encryption) - [Idle runners](#idle-runners) + - [Prebuilt Images](#prebuilt-images) - [Examples](#examples) - [Sub modules](#sub-modules) - [ARM64 configuration for submodules](#arm64-configuration-for-submodules) @@ -265,6 +266,10 @@ idle_config = [{ }] ``` +### Prebuilt Images + +This module also allows you to run agents from a prebuilt AMI to gain faster startup times. You can find more information in [the image README.md](/images/README.md) + #### Supported config Cron expressions are parsed by [cron-parser](https://github.com/harrisiirak/cron-parser#readme). The supported syntax. From 46918e2dea3677245cdc69cab19c7672980138e0 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 16:34:40 +0100 Subject: [PATCH 29/36] Add an example of deploying with prebuilt images --- .github/workflows/terraform.yml | 2 +- README.md | 1 + examples/prebuilt/.terraform.lock.hcl | 60 ++++++++++++++++++++++ examples/prebuilt/README.md | 47 +++++++++++++++++ examples/prebuilt/lambdas-download/main.tf | 25 +++++++++ examples/prebuilt/main.tf | 44 ++++++++++++++++ examples/prebuilt/outputs.tf | 15 ++++++ examples/prebuilt/providers.tf | 3 ++ examples/prebuilt/variables.tf | 6 +++ examples/prebuilt/versions.tf | 15 ++++++ examples/prebuilt/vpc.tf | 7 +++ 11 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 examples/prebuilt/.terraform.lock.hcl create mode 100644 examples/prebuilt/README.md create mode 100644 examples/prebuilt/lambdas-download/main.tf create mode 100644 examples/prebuilt/main.tf create mode 100644 examples/prebuilt/outputs.tf create mode 100644 examples/prebuilt/providers.tf create mode 100644 examples/prebuilt/variables.tf create mode 100644 examples/prebuilt/versions.tf create mode 100644 examples/prebuilt/vpc.tf diff --git a/.github/workflows/terraform.yml b/.github/workflows/terraform.yml index 450976e2e1..273280b545 100644 --- a/.github/workflows/terraform.yml +++ b/.github/workflows/terraform.yml @@ -43,7 +43,7 @@ jobs: fail-fast: false matrix: terraform: [0.14.3, 0.15.5, 1.0.8] - example: ["default", "ubuntu"] + example: ["default", "ubuntu", "prebuilt"] defaults: run: working-directory: examples/${{ matrix.example }} diff --git a/README.md b/README.md index 4e292f6ae1..4c71bef63f 100644 --- a/README.md +++ b/README.md @@ -294,6 +294,7 @@ Examples are located in the [examples](./examples) directory. The following exam - _[Default](examples/default/README.md)_: The default example of the module - _[Permissions boundary](examples/permissions-boundary/README.md)_: Example usages of permissions boundaries. +- _[Prebuilt Images](examples/prebuilt/README.md)_: Example usages of deploying runners with a custom prebuilt image. ## Sub modules diff --git a/examples/prebuilt/.terraform.lock.hcl b/examples/prebuilt/.terraform.lock.hcl new file mode 100644 index 0000000000..ba82aa4f3a --- /dev/null +++ b/examples/prebuilt/.terraform.lock.hcl @@ -0,0 +1,60 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "3.61.0" + constraints = ">= 3.27.0, >= 3.38.0" + hashes = [ + "h1:YZaozdn4J1Ax01NkubRAdv809vlpJOXRBC4KsqBzpvA=", + "h1:fpZ14qQnn+uEOO2ZOlBFHgty48Ol8IOwd+ewxZ4z3zc=", + "zh:0483ca802ddb0ae4f73144b4357ba72242c6e2641aeb460b1aa9a6f6965464b0", + "zh:274712214ebeb0c1269cbc468e5705bb5741dc45b05c05e9793ca97f22a1baa1", + "zh:3c6bd97a2ca809469ae38f6893348386c476cb3065b120b785353c1507401adf", + "zh:53dd41a9aed9860adbbeeb71a23e4f8195c656fd15a02c90fa2d302a5f577d8c", + "zh:65c639c547b97bc880fd83e65511c0f4bbfc91b63cada3b8c0d5776444221700", + "zh:a2769e19137ff480c1dd3e4f248e832df90fb6930a22c66264d9793895161714", + "zh:a5897a99332cc0071e46a71359b86a8e53ab09c1453e94cd7cf45a0b577ff590", + "zh:bdc2353642d16d8e2437a9015cd4216a1772be9736645cc17d1a197480e2b5b7", + "zh:cbeace1deae938f6c0aca3734e6088f3633ca09611aff701c15cb6d42f2b918a", + "zh:d33ca19012aabd98cc03fdeccd0bd5ce56e28f61a1dfbb2eea88e89487de7fb3", + "zh:d548b29a864b0687e85e8a993f208e25e3ecc40fcc5b671e1985754b32fdd658", + ] +} + +provider "registry.terraform.io/hashicorp/local" { + version = "2.1.0" + hashes = [ + "h1:/OpJKWupvFd8WJX1mTt8vi01pP7dkA6e//4l4C3TExE=", + "h1:KfieWtVyGWwplSoLIB5usKAUnrIkDQBkWaR5TI+4WYg=", + "zh:0f1ec65101fa35050978d483d6e8916664b7556800348456ff3d09454ac1eae2", + "zh:36e42ac19f5d68467aacf07e6adcf83c7486f2e5b5f4339e9671f68525fc87ab", + "zh:6db9db2a1819e77b1642ec3b5e95042b202aee8151a0256d289f2e141bf3ceb3", + "zh:719dfd97bb9ddce99f7d741260b8ece2682b363735c764cac83303f02386075a", + "zh:7598bb86e0378fd97eaa04638c1a4c75f960f62f69d3662e6d80ffa5a89847fe", + "zh:ad0a188b52517fec9eca393f1e2c9daea362b33ae2eb38a857b6b09949a727c1", + "zh:c46846c8df66a13fee6eff7dc5d528a7f868ae0dcf92d79deaac73cc297ed20c", + "zh:dc1a20a2eec12095d04bf6da5321f535351a594a636912361db20eb2a707ccc4", + "zh:e57ab4771a9d999401f6badd8b018558357d3cbdf3d33cc0c4f83e818ca8e94b", + "zh:ebdcde208072b4b0f8d305ebf2bfdc62c926e0717599dcf8ec2fd8c5845031c3", + "zh:ef34c52b68933bedd0868a13ccfd59ff1c820f299760b3c02e008dc95e2ece91", + ] +} + +provider "registry.terraform.io/hashicorp/random" { + version = "3.1.0" + hashes = [ + "h1:EPIax4Ftp2SNdB9pUfoSjxoueDoLc/Ck3EUoeX0Dvsg=", + "h1:rKYu5ZUbXwrLG1w81k7H3nce/Ys6yAxXhWcbtk36HjY=", + "zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc", + "zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626", + "zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff", + "zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2", + "zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992", + "zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427", + "zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc", + "zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f", + "zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b", + "zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7", + "zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a", + ] +} diff --git a/examples/prebuilt/README.md b/examples/prebuilt/README.md new file mode 100644 index 0000000000..198c13841b --- /dev/null +++ b/examples/prebuilt/README.md @@ -0,0 +1,47 @@ +# Action runners deployment with prebuilt image + +This module shows how to create GitHub action runners using a prebuilt AMI for the runners + +## Usages + +Steps for the full setup, such as creating a GitHub app can be found in the root module's [README](../../README.md). + +### Lambdas + +You can either download the released lambda code or build them locally yourself. + +First download the Lambda releases from GitHub. Ensure you have set the version in `lambdas-download/main.tf` for running the example. The version needs to be set to a GitHub release version, see https://github.com/philips-labs/terraform-aws-github-runner/releases + +```bash +cd lambdas-download +terraform init +terraform apply +cd .. +``` + +Alternatively you can build the lambdas locally with Node or Docker, there is a simple build script in `/.ci/build.sh`. In the `main.tf` you can simply remove the location of the lambda zip files, the default location will work in this case. + +### GitHub App Configuration + +Before running Terraform, ensure the GitHub app is configured. See the [configuration details](../../README.md#usages) for more details. + +### Packer Image + +You will need to build your image. This example deployment uses the image example in `/images/linux-amz2`. You must build this image with packer in your AWS account first. Once you have built this you need to provider your owner ID as a variable + +## Deploy + +You can then deploy the terraform + +```bash +terraform init +terraform apply +``` + +You can receive the webhook details by running: + +```bash +terraform output -raw webhook_secret +``` + +Be-aware some shells will print some end of line character `%`. diff --git a/examples/prebuilt/lambdas-download/main.tf b/examples/prebuilt/lambdas-download/main.tf new file mode 100644 index 0000000000..87f31bd8a9 --- /dev/null +++ b/examples/prebuilt/lambdas-download/main.tf @@ -0,0 +1,25 @@ +locals { + version = "" +} + +module "lambdas" { + source = "../../../modules/download-lambda" + lambdas = [ + { + name = "webhook" + tag = local.version + }, + { + name = "runners" + tag = local.version + }, + { + name = "runner-binaries-syncer" + tag = local.version + } + ] +} + +output "files" { + value = module.lambdas.files +} diff --git a/examples/prebuilt/main.tf b/examples/prebuilt/main.tf new file mode 100644 index 0000000000..1b579ed8df --- /dev/null +++ b/examples/prebuilt/main.tf @@ -0,0 +1,44 @@ +locals { + environment = "prebuilt" + aws_region = "eu-west-1" +} + +resource "random_password" "random" { + length = 28 +} + +module "runners" { + source = "../../" + create_service_linked_role_spot = true + aws_region = local.aws_region + vpc_id = module.vpc.vpc_id + subnet_ids = module.vpc.private_subnets + + environment = local.environment + + github_app = { + key_base64 = var.github_app_key_base64 + id = var.github_app_id + webhook_secret = random_password.random.result + } + + 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" + + # configure your pre-built AMI + userdata_enabled = false + ami_filter = { name = ["github-runner-amzn2-2021*"] } + ami_owners = [var.ami_owner] + + # enable access to the runners via SSM + enable_ssm_on_runners = true + + # override delay of events in seconds + delay_webhook_event = 0 + + # override scaling down + scale_down_schedule_expression = "cron(* * * * ? *)" +} diff --git a/examples/prebuilt/outputs.tf b/examples/prebuilt/outputs.tf new file mode 100644 index 0000000000..d6886efe36 --- /dev/null +++ b/examples/prebuilt/outputs.tf @@ -0,0 +1,15 @@ +output "runners" { + value = { + lambda_syncer_name = module.runners.binaries_syncer.lambda.function_name + } +} + +output "webhook_endpoint" { + value = module.runners.webhook.endpoint +} + +output "webhook_secret" { + sensitive = true + value = random_password.random.result +} + diff --git a/examples/prebuilt/providers.tf b/examples/prebuilt/providers.tf new file mode 100644 index 0000000000..b6c81d5415 --- /dev/null +++ b/examples/prebuilt/providers.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = local.aws_region +} diff --git a/examples/prebuilt/variables.tf b/examples/prebuilt/variables.tf new file mode 100644 index 0000000000..562af65488 --- /dev/null +++ b/examples/prebuilt/variables.tf @@ -0,0 +1,6 @@ + +variable "github_app_key_base64" {} + +variable "github_app_id" {} + +variable "ami_owner" {} diff --git a/examples/prebuilt/versions.tf b/examples/prebuilt/versions.tf new file mode 100644 index 0000000000..c96d0eee84 --- /dev/null +++ b/examples/prebuilt/versions.tf @@ -0,0 +1,15 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 3.27" + } + local = { + source = "hashicorp/local" + } + random = { + source = "hashicorp/random" + } + } + required_version = ">= 0.14" +} diff --git a/examples/prebuilt/vpc.tf b/examples/prebuilt/vpc.tf new file mode 100644 index 0000000000..a7d21422f1 --- /dev/null +++ b/examples/prebuilt/vpc.tf @@ -0,0 +1,7 @@ +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 + create_private_hosted_zone = false +} From a76731fb293a474248a60bdc69736ba42e88b2e7 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Thu, 2 Dec 2021 16:42:27 +0100 Subject: [PATCH 30/36] Update readme --- examples/prebuilt/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/examples/prebuilt/README.md b/examples/prebuilt/README.md index 198c13841b..cbdaebd8bc 100644 --- a/examples/prebuilt/README.md +++ b/examples/prebuilt/README.md @@ -19,7 +19,13 @@ terraform apply cd .. ``` -Alternatively you can build the lambdas locally with Node or Docker, there is a simple build script in `/.ci/build.sh`. In the `main.tf` you can simply remove the location of the lambda zip files, the default location will work in this case. +Alternatively you can build the lambdas locally with Node or Docker, there is a simple build script in `/.ci/build.sh`. In the `main.tf` you need to specify the build location for all of the zip files. + +```hcl + 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" +``` ### GitHub App Configuration From 55004cdeae559039416e8a6aa4f0448ac16f0c18 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 3 Dec 2021 09:44:29 +0100 Subject: [PATCH 31/36] Use current user as ami_owner --- examples/prebuilt/main.tf | 4 +++- examples/prebuilt/variables.tf | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/prebuilt/main.tf b/examples/prebuilt/main.tf index 1b579ed8df..05aee4751d 100644 --- a/examples/prebuilt/main.tf +++ b/examples/prebuilt/main.tf @@ -7,6 +7,8 @@ resource "random_password" "random" { length = 28 } +data "aws_caller_identity" "current" {} + module "runners" { source = "../../" create_service_linked_role_spot = true @@ -31,7 +33,7 @@ module "runners" { # configure your pre-built AMI userdata_enabled = false ami_filter = { name = ["github-runner-amzn2-2021*"] } - ami_owners = [var.ami_owner] + ami_owners = [data.aws_caller_identity.current.account_id] # enable access to the runners via SSM enable_ssm_on_runners = true diff --git a/examples/prebuilt/variables.tf b/examples/prebuilt/variables.tf index 562af65488..69dcd0c61c 100644 --- a/examples/prebuilt/variables.tf +++ b/examples/prebuilt/variables.tf @@ -2,5 +2,3 @@ variable "github_app_key_base64" {} variable "github_app_id" {} - -variable "ami_owner" {} From cf44a5e6c93587b123fe04059a3eeed1f6c1ba2c Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 3 Dec 2021 09:54:58 +0100 Subject: [PATCH 32/36] Update example to 5 secs --- examples/prebuilt/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/prebuilt/main.tf b/examples/prebuilt/main.tf index 05aee4751d..f5ae7bb7d7 100644 --- a/examples/prebuilt/main.tf +++ b/examples/prebuilt/main.tf @@ -39,7 +39,7 @@ module "runners" { enable_ssm_on_runners = true # override delay of events in seconds - delay_webhook_event = 0 + delay_webhook_event = 5 # override scaling down scale_down_schedule_expression = "cron(* * * * ? *)" From c155774639ac39e97a0e24014c50a1ad06568d43 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 3 Dec 2021 10:02:30 +0100 Subject: [PATCH 33/36] Updated ami name to include the arch --- examples/prebuilt/main.tf | 2 +- images/README.md | 4 ++-- images/linux-amzn2/github_agent.linux.pkr.hcl | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/prebuilt/main.tf b/examples/prebuilt/main.tf index f5ae7bb7d7..b0ca49ab2f 100644 --- a/examples/prebuilt/main.tf +++ b/examples/prebuilt/main.tf @@ -32,7 +32,7 @@ module "runners" { # configure your pre-built AMI userdata_enabled = false - ami_filter = { name = ["github-runner-amzn2-2021*"] } + ami_filter = { name = ["github-runner-amzn2-x86_64-2021*"] } ami_owners = [data.aws_caller_identity.current.account_id] # enable access to the runners via SSM diff --git a/images/README.md b/images/README.md index cc0c6e70ab..09c382cc94 100644 --- a/images/README.md +++ b/images/README.md @@ -25,11 +25,11 @@ Your image will then begin to build inside AWS and when finished you will be pro To use your image in the terraform modules you will need to set some values on the module. -Assuming you have built the `linux-amzn2` image which has a pre-defined AMI name in the following format `github-runner-amzn2-YYYYMMDDhhmm` you can use the following values. +Assuming you have built the `linux-amzn2` image which has a pre-defined AMI name in the following format `github-runner-amzn2-x86_64-YYYYMMDDhhmm` you can use the following values. ```hcl # set the name of the ami to use -ami_filter = { name = ["github-runner-amzn2-2021*"] } +ami_filter = { name = ["github-runner-amzn2-x86_64-2021*"] } # provide the owner id of ami_owners = [""] diff --git a/images/linux-amzn2/github_agent.linux.pkr.hcl b/images/linux-amzn2/github_agent.linux.pkr.hcl index 57f42f777c..566423d03f 100644 --- a/images/linux-amzn2/github_agent.linux.pkr.hcl +++ b/images/linux-amzn2/github_agent.linux.pkr.hcl @@ -20,7 +20,7 @@ variable "region" { } source "amazon-ebs" "githubrunner" { - ami_name = "github-runner-amzn2-${formatdate("YYYYMMDDhhmm", timestamp())}" + ami_name = "github-runner-amzn2-x86_64-${formatdate("YYYYMMDDhhmm", timestamp())}" instance_type = "m3.medium" region = var.region source_ami_filter { From 5d5aaa44bcaf5019c1876bc7dad2b81eca58b5ea Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 3 Dec 2021 10:02:45 +0100 Subject: [PATCH 34/36] Fixed log file variable --- modules/runners/variables.tf | 10 +++++----- variables.tf | 8 +++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index 643050b287..779800970d 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -299,17 +299,17 @@ variable "runner_log_files" { "log_stream_name" : "{instance_id}" }, { - "log_group_name" : "runner-startup", + "log_group_name" : "runner", "prefix_log_group" : true, - "file_path" : "/var/log/runner-startup.log", + "file_path" : "/home/ec2-user/actions-runner/_diag/Runner_**.log", "log_stream_name" : "{instance_id}" }, { - "log_group_name" : "runner", + "log_group_name" : "runner-startup", "prefix_log_group" : true, - "file_path" : "/home/ec2-user/actions-runner/_diag/Runner_**.log", + "file_path" : "/var/log/runner-startup.log", "log_stream_name" : "{instance_id}" - } + }, ] } diff --git a/variables.tf b/variables.tf index 2f171057ba..0eef3a4972 100644 --- a/variables.tf +++ b/variables.tf @@ -319,7 +319,13 @@ variable "runner_log_files" { "prefix_log_group" : true, "file_path" : "/home/ec2-user/actions-runner/_diag/Runner_**.log", "log_stream_name" : "{instance_id}" - } + }, + { + "log_group_name" : "runner-startup", + "prefix_log_group" : true, + "file_path" : "/var/log/runner-startup.log", + "log_stream_name" : "{instance_id}" + }, ] } From 49b8a5c01b5d7f3b65e10f0dbb6ff8c9ff1df9c9 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 3 Dec 2021 10:03:04 +0100 Subject: [PATCH 35/36] Added explicit info about required settings to the readme --- examples/prebuilt/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/examples/prebuilt/README.md b/examples/prebuilt/README.md index cbdaebd8bc..f2a5d6ae60 100644 --- a/examples/prebuilt/README.md +++ b/examples/prebuilt/README.md @@ -37,6 +37,36 @@ You will need to build your image. This example deployment uses the image exampl ## Deploy +To use your image in the terraform modules you will need to set some values on the module. + +Assuming you have built the `linux-amzn2` image which has a pre-defined AMI name in the following format `github-runner-amzn2-x86_64-YYYYMMDDhhmm` you can use the following values. + +```hcl + +module "runners" { + ... + # set the name of the ami to use + ami_filter = { name = ["github-runner-amzn2-x86_64-2021*"] } + # provide the owner id of + ami_owners = [""] + + userdata_enabled = false + ... +} +``` + +If your owner is the same as the account you are logging into then you can use `aws_caller_identity` to retrieve it dynamically. + +```hcl +data "aws_caller_identity" "current" {} + +module "runners" { + ... + ami_owners = [data.aws_caller_identity.current.account_id] + ... +} +``` + You can then deploy the terraform ```bash From 8e987a863b58b060ae8fd0ca7c0e1c6907c6f8d5 Mon Sep 17 00:00:00 2001 From: Scott Guymer Date: Fri, 3 Dec 2021 15:49:59 +0100 Subject: [PATCH 36/36] Change userdata_enabled to enabled_userdata Keep within existing naming convention --- README.md | 2 +- examples/prebuilt/README.md | 2 +- examples/prebuilt/main.tf | 8 ++++---- images/README.md | 2 +- main.tf | 2 +- modules/runners/README.md | 2 +- modules/runners/main.tf | 2 +- modules/runners/variables.tf | 2 +- variables.tf | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4c71bef63f..02a7f79fb5 100644 --- a/README.md +++ b/README.md @@ -428,7 +428,7 @@ In case the setup does not work as intended follow the trace of events: | [syncer\_lambda\_s3\_key](#input\_syncer\_lambda\_s3\_key) | S3 key for syncer lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no | | [syncer\_lambda\_s3\_object\_version](#input\_syncer\_lambda\_s3\_object\_version) | S3 object version for syncer lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no | | [tags](#input\_tags) | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no | -| [userdata\_enabled](#input\_userdata\_enabled) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | +| [enabled_userdata](#input\_enabled_userdata) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | | [userdata\_post\_install](#input\_userdata\_post\_install) | Script to be ran after the GitHub Actions runner is installed on the EC2 instances | `string` | `""` | no | | [userdata\_pre\_install](#input\_userdata\_pre\_install) | Script to be ran before the GitHub Actions runner is installed on the EC2 instances | `string` | `""` | no | | [userdata\_template](#input\_userdata\_template) | Alternative user-data template, replacing the default template. By providing your own user\_data you have to take care of installing all required software, including the action runner. Variables userdata\_pre/post\_install are ignored. | `string` | `null` | no | diff --git a/examples/prebuilt/README.md b/examples/prebuilt/README.md index f2a5d6ae60..3a560fb80d 100644 --- a/examples/prebuilt/README.md +++ b/examples/prebuilt/README.md @@ -50,7 +50,7 @@ module "runners" { # provide the owner id of ami_owners = [""] - userdata_enabled = false + enabled_userdata = false ... } ``` diff --git a/examples/prebuilt/main.tf b/examples/prebuilt/main.tf index b0ca49ab2f..f67c5d984d 100644 --- a/examples/prebuilt/main.tf +++ b/examples/prebuilt/main.tf @@ -24,14 +24,14 @@ module "runners" { webhook_secret = random_password.random.result } - 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" + 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" runner_extra_labels = "default,example" # configure your pre-built AMI - userdata_enabled = false + enabled_userdata = false ami_filter = { name = ["github-runner-amzn2-x86_64-2021*"] } ami_owners = [data.aws_caller_identity.current.account_id] diff --git a/images/README.md b/images/README.md index 09c382cc94..172f97d0a1 100644 --- a/images/README.md +++ b/images/README.md @@ -33,5 +33,5 @@ ami_filter = { name = ["github-runner-amzn2-x86_64-2021*"] } # provide the owner id of ami_owners = [""] -userdata_enabled = false +enabled_userdata = false ``` diff --git a/main.tf b/main.tf index da49358bbb..6708b4cd06 100644 --- a/main.tf +++ b/main.tf @@ -126,7 +126,7 @@ module "runners" { role_path = var.role_path role_permissions_boundary = var.role_permissions_boundary - userdata_enabled = var.userdata_enabled + enabled_userdata = var.enabled_userdata userdata_template = var.userdata_template userdata_pre_install = var.userdata_pre_install userdata_post_install = var.userdata_post_install diff --git a/modules/runners/README.md b/modules/runners/README.md index bf6fee8346..05cb8b2c42 100644 --- a/modules/runners/README.md +++ b/modules/runners/README.md @@ -164,7 +164,7 @@ No modules. | [sqs\_build\_queue](#input\_sqs\_build\_queue) | SQS queue to consume accepted build events. |
object({
arn = string
})
| n/a | yes | | [subnet\_ids](#input\_subnet\_ids) | List of subnets in which the action runners will be launched, the subnets needs to be subnets in the `vpc_id`. | `list(string)` | n/a | yes | | [tags](#input\_tags) | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no | -| [userdata\_enabled](#input\_userdata\_enabled) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | +| [enabled\_userdata](#input\_enabled_userdata) | Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI | `bool` | `true` | no | | [userdata\_post\_install](#input\_userdata\_post\_install) | User-data script snippet to insert after GitHub action runner install | `string` | `""` | no | | [userdata\_pre\_install](#input\_userdata\_pre\_install) | User-data script snippet to insert before GitHub action runner install | `string` | `""` | no | | [userdata\_template](#input\_userdata\_template) | Alternative user-data template, replacing the default template. By providing your own user\_data you have to take care of installing all required software, including the action runner. Variables userdata\_pre/post\_install are ignored. | `string` | `null` | no | diff --git a/modules/runners/main.tf b/modules/runners/main.tf index 22d432fb88..8a48fece0e 100644 --- a/modules/runners/main.tf +++ b/modules/runners/main.tf @@ -110,7 +110,7 @@ resource "aws_launch_template" "runner" { } - user_data = var.userdata_enabled ? base64encode(templatefile(local.userdata_template, { + user_data = var.enabled_userdata ? base64encode(templatefile(local.userdata_template, { pre_install = var.userdata_pre_install install_runner = templatefile(local.userdata_install_runner, { S3_LOCATION_RUNNER_DISTRIBUTION = var.s3_location_runner_binaries diff --git a/modules/runners/variables.tf b/modules/runners/variables.tf index 779800970d..c349557fde 100644 --- a/modules/runners/variables.tf +++ b/modules/runners/variables.tf @@ -84,7 +84,7 @@ variable "ami_owners" { default = ["amazon"] } -variable "userdata_enabled" { +variable "enabled_userdata" { description = "Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI" type = bool default = true diff --git a/variables.tf b/variables.tf index 0eef3a4972..a627a067f2 100644 --- a/variables.tf +++ b/variables.tf @@ -165,7 +165,7 @@ variable "kms_key_arn" { default = null } -variable "userdata_enabled" { +variable "enabled_userdata" { description = "Should the userdata script be enabled for the runner. Set this to false if you are using your own prebuilt AMI" type = bool default = true