├── .github ├── ISSUE_TEMPLATE │ ├── create-edit-self-hosted-runner-scale-set.md │ └── delete-self-hosted-runner-scale-set.md └── workflows │ ├── approve-self-hosted-runner-create-edit.yaml │ ├── approve-self-hosted-runner-delete-archive.yaml │ ├── create-edit-self-hosted-runner-issue.yml │ ├── delete-self-hosted-runner-issue.yml │ └── test-runners-example.yaml ├── .gitignore ├── README.md ├── actions-runner-envs-archived ├── link │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── terraform.tfvars │ ├── test-auto-scaling-workflow.yml │ ├── variables.tf │ └── vpc.tf ├── rex │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── terraform.tfvars │ ├── test-auto-scaling-workflow.yml │ ├── variables.tf │ └── vpc.tf └── zelda │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── terraform.tfvars │ ├── test-auto-scaling-workflow.yml │ ├── variables.tf │ └── vpc.tf ├── actions-runner-envs ├── download-lambdas │ └── main.tf ├── example │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── terraform.tfvars │ ├── test-auto-scaling-workflow.yml │ ├── variables.tf │ └── vpc.tf └── template-env │ ├── backend.tf │ ├── main.tf │ ├── outputs.tf │ ├── providers.tf │ ├── terraform.tfvars │ ├── test-auto-scaling-workflow.yml │ ├── variables.tf │ └── vpc.tf ├── bootstrap-terraform-state-bucket ├── main.tf ├── outputs.tf └── variables.tf ├── custom-images └── actions-runner-with-additional-packages.json └── docs ├── architecture.drawio ├── architecture.svg └── component-overview.svg /.github/ISSUE_TEMPLATE/create-edit-self-hosted-runner-scale-set.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Create or edit self-hosted runner scale set 3 | about: Create or edit a self-hosted runner scale set you can use for a workflow 4 | title: Self-hosted runner scale set create/update request for 5 | labels: 'self-hosted-runner-scale-set' 6 | 7 | --- 8 | 9 | Issue created by this issue template will create or edit the necessary AWS configuration needed for a self-hosted runner scale set running in its own environment operated by [Philips Labs's AWS GitHub Runner](https://github.com/philips-labs/terraform-aws-github-runner). 10 | 11 | Please insert the runner configuration values in the JSON below. It is advised but not mandatory that you create a runner group with the name of your environment first to control which repositories have access to your runner scale set. 12 | 13 | 14 | **In order to be mindful of resources, please do not set the number of `maxRunners` per environment greater than 10.** 15 | 16 | ```json 17 | { 18 | "environment": "", 19 | "organization": "", 20 | "minRunners": "1", 21 | "maxRunners": "3", 22 | "runnerGroup": "default", 23 | "amiFilter": "amzn2-ami-hvm-2.*-x86_64-ebs", 24 | "amiOwner": "amazon" 25 | } 26 | ``` 27 | 28 | If you like to use the standard Amazon runners, set 29 | amiOwner to "amazon" and amiFilter to "amzn2-ami-hvm-2.*-x86_64-ebs" 30 | 31 | Environments should not contain the term "aws" (Amazon forbids that) and only contain numbers, characters and underscores. 32 | 33 | ![image](https://user-images.githubusercontent.com/1872314/106927306-52228b80-6712-11eb-9cb8-11e91d719b9a.png) 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/delete-self-hosted-runner-scale-set.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Delete self-hosted runner scale set 3 | about: Deletes and archives a self-hosted runner scale set 4 | title: Self-hosted runner scale set delete/archive request for 5 | labels: 'delete-self-hosted-runner-scale-set' 6 | 7 | --- 8 | 9 | Issue created by this issue template will delete and archive the AWS configuration associated with the self-hosted runner scale set operated by [Philips Labs's AWS GitHub Runner](https://github.com/philips-labs/terraform-aws-github-runner). 10 | 11 | 12 | Please insert the runner configuration values in the JSON below. 13 | ```json 14 | { 15 | "environment": "" 16 | } 17 | ``` 18 | -------------------------------------------------------------------------------- /.github/workflows/approve-self-hosted-runner-create-edit.yaml: -------------------------------------------------------------------------------- 1 | name: "Approve self-hosted runner scale set creation/update request" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | 6 | jobs: 7 | prechecks: 8 | name: "Check pre-conditions for self hosted runner scale set creation/update" 9 | if: github.event.issue.pull_request != null && startsWith(github.event.issue.title, 'Create or edit self hosted runner scale set for env ') && startsWith(github.event.comment.body, '/approve') 10 | runs-on: Linux 11 | outputs: 12 | environment: ${{ steps.prechecks.outputs.environment }} 13 | steps: 14 | - name: Debug 15 | uses: actions/github-script@v3 16 | with: 17 | script: console.log(JSON.stringify(context, null, 2)); 18 | - name: Check permissions and scan environment 19 | id: prechecks 20 | uses: actions/github-script@v3 21 | env: 22 | title: ${{ github.event.issue.title }} 23 | comment: ${{ github.event.issue.body }} 24 | with: 25 | github-token: ${{secrets.GITHUB_TOKEN}} 26 | script: | 27 | const { title } = process.env; 28 | let envMatch = title.match(/^Create or edit self hosted runner scale set for env ([a-zA-Z0-9_]+)$/) 29 | if(!envMatch || envMatch[1].trim() === '') { 30 | message = '👋 @' + context.actor + ', seems as if you have not specified an environment in the issue title, please make sure to follow the issue template for self hosted runner scale set creation and updates.' 31 | core.setOutput('error', message) 32 | throw new Error(message) 33 | } 34 | const environment = envMatch[1] 35 | core.info("environment: " + environment) 36 | const permissionRes = await github.repos.getCollaboratorPermissionLevel( 37 | { 38 | ...context.repo, 39 | username: context.actor 40 | } 41 | ) 42 | if (permissionRes.status !== 200) { 43 | message = 'Permission check returns non-200 status: ${permissionRes.status}' 44 | core.setOutput('error', message) 45 | throw new Error(message) 46 | } 47 | const actorPermission = permissionRes.data.permission 48 | if (!['admin', 'write'].includes(actorPermission)) { 49 | message = '👋 @' + context.actor + ', seems as if you have not admin/write permission to /approve this PR, permissions: ${actorPermission}' 50 | core.setOutput('error', message) 51 | throw new Error(message) 52 | } 53 | core.setOutput('environment', environment) 54 | 55 | - name: Pre-Check-Failed 56 | id: precheck-failed 57 | if: failure() 58 | uses: actions/github-script@v3 59 | env: 60 | message: ${{steps.prechecks.outputs.error}} 61 | with: 62 | github-token: ${{secrets.GITHUB_TOKEN}} 63 | script: | 64 | const { message } = process.env; 65 | github.issues.createComment({ 66 | ...context.repo, 67 | issue_number: context.issue.number, 68 | body: message 69 | }) 70 | 71 | act-on-provisioning-request: 72 | name: "Provision self hosted runner scale set" 73 | needs: [prechecks] 74 | 75 | env: 76 | TF_VAR_github_app_client_secret: ${{ secrets.APP_CLIENT_SECRET }} 77 | TF_VAR_github_app_key_base64: ${{ secrets.APP_KEY_BASE64 }} 78 | TF_VAR_github_app_id: ${{ secrets.APP_ID }} 79 | TF_VAR_github_app_client_id: ${{ secrets.APP_CLIENT_ID }} 80 | TF_VAR_github_enterprise_url: ${{ secrets.ENTERPRISE_SERVER_URL }} 81 | TF_IN_AUTOMATION: true 82 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 83 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 84 | environment: ${{needs.prechecks.outputs.environment}} 85 | 86 | timeout-minutes: 10 87 | 88 | runs-on: Linux 89 | steps: 90 | 91 | - name: Acknowledge self hosted runner provisioning request 92 | id: acknowledge 93 | uses: actions/github-script@v3 94 | with: 95 | github-token: ${{secrets.GITHUB_TOKEN}} 96 | script: | 97 | const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}` 98 | const { environment } = process.env; 99 | pr = await github.pulls.get( 100 | { 101 | ...context.repo, 102 | pull_number: context.issue.number 103 | } 104 | ) 105 | 106 | if (pr.status !== 200) { 107 | message = 'Could not retrieve PR info: ${permissionRes.status}' 108 | core.setOutput('error', message) 109 | throw new Error(message) 110 | } 111 | 112 | if (!pr.data.mergeable) { 113 | message = 'This PR is currently not mergeable, please update it with the latest changes from the target branch.' 114 | core.setOutput('error', message) 115 | throw new Error(message) 116 | } 117 | 118 | github.issues.createComment({ 119 | ...context.repo, 120 | issue_number: context.issue.number, 121 | body: '👋 @' + context.actor + ' provisioning self hosted runner scale set environment ' + environment + ' now, you can watch the progress [here](' + log_url + ') ...' 122 | }) 123 | 124 | core.setOutput('ref', pr.data.head.ref) 125 | 126 | - name: Checkout 127 | uses: actions/checkout@v2 128 | with: 129 | ref: ${{ steps.acknowledge.outputs.ref }} 130 | 131 | - name: Setup node 132 | uses: actions/setup-node@v2.1.2 133 | with: 134 | node-version: '12' 135 | 136 | - name: HashiCorp - Setup Terraform 137 | uses: hashicorp/setup-terraform@v1.3.2 138 | with: 139 | terraform_version: 0.14.5 140 | terraform_wrapper: true 141 | 142 | - name: Terraform Init lambdas-download 143 | id: init-lambdas 144 | run: cd actions-runner-envs/download-lambdas && terraform init 145 | 146 | - name: Terraform Plan lambdas-download 147 | id: plan-lambdas 148 | run: cd actions-runner-envs/download-lambdas && terraform plan -no-color -out lambdas-download.plan 149 | continue-on-error: false 150 | 151 | - name: Terraform Apply lambdas-download 152 | id: apply 153 | run: cd actions-runner-envs/download-lambdas && terraform apply -no-color -input=false -auto-approve lambdas-download.plan 154 | continue-on-error: false 155 | 156 | - name: Terraform Init AWS environment 157 | id: init-aws 158 | run: cd actions-runner-envs/$environment && terraform init 159 | 160 | - name: Terraform Plan AWS environment 161 | id: plan-aws 162 | run: cd actions-runner-envs/$environment && terraform plan -no-color -out ${environment}.plan 163 | continue-on-error: false 164 | 165 | - name: Terraform Apply AWS environment changes 166 | id: apply-changes 167 | run: | 168 | cd actions-runner-envs/$environment 169 | IFS=";" SAMPLE_WORKFLOW=$(cat test-auto-scaling-workflow.yml) 170 | SAMPLE_WORKFLOW="${SAMPLE_WORKFLOW//'%'/'%25'}" 171 | SAMPLE_WORKFLOW="${SAMPLE_WORKFLOW//$'\n'/'%0A'}" 172 | SAMPLE_WORKFLOW="${SAMPLE_WORKFLOW//$'\r'/'%0D'}" 173 | echo "::set-output name=sample-workflow::$SAMPLE_WORKFLOW" 174 | 175 | terraform apply -no-color -input=false -auto-approve ${environment}.plan 176 | continue-on-error: false 177 | 178 | - name: Terraform Retrieve Webhook config 179 | id: retrieve-webhook-config 180 | run: cd actions-runner-envs/$environment && terraform output webhook 181 | 182 | - name: Provisioning Succeeded 183 | id: provisioning-succeeded 184 | if: success() 185 | uses: actions/github-script@v3 186 | env: 187 | CONFIG_APPLIED: ${{ steps.apply-changes.outputs.stdout }} 188 | SAMPLE_WORKFLOW: ${{ steps.apply-changes.outputs.sample-workflow }} 189 | WEBHOOK_DETAILS: ${{ steps.retrieve-webhook-config.outputs.stdout }} 190 | PLAN: ${{ steps.plan-aws.outputs.stdout }} 191 | with: 192 | github-token: ${{ secrets.GITHUB_TOKEN }} 193 | script: | 194 | const commentBody = `\ 195 | ### :computer: Self-hosted runner configuration applied successfully :runner: 196 | 197 | In order to make use of the configuration, [install this GitHub app](https://octodemo.com/github-apps/terraform-aws-github-runner/installations/new) in your org and create an org webhook to send the \`check_run\` event and set the following parameters: 198 | ${process.env.WEBHOOK_DETAILS} 199 | 200 |
201 | 📖 Terraform plan 202 | 203 | \`\`\` 204 | ${process.env.PLAN} 205 | \`\`\` 206 |
207 | 208 |
209 | 210 | 🚀 Plan applied 211 | 212 | \`\`\` 213 | ${process.env.CONFIG_APPLIED} 214 | \`\`\` 215 |
216 | 217 |
218 | :octocat: Auto-scaling test workflow (paste into .github/workflows directory of target repo) 219 | 220 | \`\`\` 221 | ${process.env.SAMPLE_WORKFLOW} 222 | \`\`\` 223 |
224 | 225 | Merging this PR now ... 226 | 227 | After merging, you can also copy the GitHub Action sample workflow from [here](../blob/master/actions-runner-envs/${{needs.prechecks.outputs.environment}}/test-auto-scaling-workflow.yml). 228 | To change or delete the runner scale set configuration, open another issue, or reopen the last issue with a modified description. 229 | `; 230 | 231 | await github.issues.createComment({ 232 | ...context.repo, 233 | issue_number: ${{ github.event.issue.number }}, 234 | body: commentBody 235 | }) 236 | 237 | github.pulls.merge({ 238 | ...context.repo, 239 | pull_number: context.issue.number 240 | }); 241 | 242 | - name: Provisioning Failed 243 | id: provisioning-failed 244 | if: cancelled() || failure() 245 | uses: actions/github-script@v3 246 | with: 247 | github-token: ${{ secrets.GITHUB_TOKEN }} 248 | script: | 249 | const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}` 250 | 251 | github.issues.createComment({ 252 | ...context.repo, 253 | issue_number: ${{ github.event.issue.number }}, 254 | body: `Self-hosted runner provision request failed :cry:. [View error logs](${ log_url}).` 255 | }) 256 | 257 | 258 | # Force an unlock on the Terraform state file if we were cancelled or failed (probably really only needed for cancelled). 259 | - name: Reveal Terraform Lock 260 | id: reveal_terraform_lock 261 | if: cancelled() || failure() 262 | run: cd actions-runner-envs/$environment && terraform force-unlock -force abc 263 | continue-on-error: true 264 | 265 | - name: Extract Terraform Lock ID 266 | if: cancelled() || failure() 267 | id: extract_lock 268 | uses: actions/github-script@v3 269 | env: 270 | TERRAFORM_LOCK_OUTPUT: ${{ steps.reveal_terraform_lock.outputs.stderr }} 271 | with: 272 | lock_data: ${{ steps.reveal_terraform_lock.outputs.stderr }} 273 | script: > 274 | console.log(process.env.TERRAFORM_LOCK_OUTPUT); 275 | console.log(core.getInput('lock_data')); 276 | 277 | const matched = /Lock Info:\s*ID:\s*([a-z|0-9|-]*)/gm.exec(core.getInput('lock_data')); 278 | if (matched) { 279 | core.setOutput('lock_id', matched[1]); 280 | } 281 | core.setOutput('requires_unlock', !!matched); 282 | 283 | - name: Unlock Terraform State 284 | id: unlock_terraform_state 285 | if: (cancelled() || failure()) && steps.extract_lock.outputs.requires_unlock == 'true' 286 | run: cd actions-runner-envs/$environment && terraform force-unlock -force ${{ steps.extract_lock.outputs.lock_id }} 287 | -------------------------------------------------------------------------------- /.github/workflows/approve-self-hosted-runner-delete-archive.yaml: -------------------------------------------------------------------------------- 1 | name: "Approve self-hosted runner scale set deletion/archival request" 2 | on: 3 | issue_comment: 4 | types: [created] 5 | 6 | jobs: 7 | prechecks: 8 | name: "Check pre-conditions for self hosted runner scale set deletion" 9 | if: github.event.issue.pull_request != null && startsWith(github.event.issue.title, 'Delete and archive self hosted runner scale set for env ') && startsWith(github.event.comment.body, '/approve') 10 | runs-on: Linux 11 | outputs: 12 | environment: ${{ steps.prechecks.outputs.environment }} 13 | steps: 14 | - name: Debug 15 | uses: actions/github-script@v3 16 | with: 17 | script: console.log(JSON.stringify(context, null, 2)); 18 | - name: Check permissions and scan environment 19 | id: prechecks 20 | uses: actions/github-script@v3 21 | env: 22 | title: ${{ github.event.issue.title }} 23 | comment: ${{ github.event.issue.body }} 24 | with: 25 | github-token: ${{secrets.GITHUB_TOKEN}} 26 | script: | 27 | const { title } = process.env; 28 | let envMatch = title.match(/^Delete and archive self hosted runner scale set for env ([a-zA-Z0-9_]+)$/) 29 | if(!envMatch || envMatch[1].trim() === '') { 30 | message = '👋 @' + context.actor + ', seems as if you have not specified an environment in the issue title, please make sure to follow the issue template for self hosted runner scale set deletion.' 31 | core.setOutput('error', message) 32 | throw new Error(message) 33 | } 34 | const environment = envMatch[1] 35 | core.info("environment: " + environment) 36 | const permissionRes = await github.repos.getCollaboratorPermissionLevel( 37 | { 38 | ...context.repo, 39 | username: context.actor 40 | } 41 | ) 42 | 43 | if (! /^[a-zA-Z0-9_]+$/.test(environment)) { 44 | const error = `The name of the environment contains illegal characters: ${runner.environment}`; 45 | core.error(error); 46 | core.setFailed(error); 47 | } 48 | 49 | if (permissionRes.status !== 200) { 50 | message = 'Permission check returns non-200 status: ${permissionRes.status}' 51 | core.setOutput('error', message) 52 | throw new Error(message) 53 | } 54 | const actorPermission = permissionRes.data.permission 55 | if (!['admin', 'write'].includes(actorPermission)) { 56 | message = '👋 @' + context.actor + ', seems as if you have not admin/write permission to /approve this PR, permissions: ${actorPermission}' 57 | core.setOutput('error', message) 58 | throw new Error(message) 59 | } 60 | core.setOutput('environment', environment) 61 | 62 | - name: Pre-Check-Failed 63 | id: precheck-failed 64 | if: failure() 65 | uses: actions/github-script@v3 66 | env: 67 | message: ${{steps.prechecks.outputs.error}} 68 | with: 69 | github-token: ${{secrets.GITHUB_TOKEN}} 70 | script: | 71 | const { message } = process.env; 72 | github.issues.createComment({ 73 | ...context.repo, 74 | issue_number: context.issue.number, 75 | body: message 76 | }) 77 | 78 | act-on-deprovisioning-request: 79 | name: "Deprovision self hosted runner scale set" 80 | needs: [prechecks] 81 | runs-on: Linux 82 | env: 83 | TF_VAR_github_app_client_secret: ${{ secrets.APP_CLIENT_SECRET }} 84 | TF_VAR_github_app_key_base64: ${{ secrets.APP_KEY_BASE64 }} 85 | TF_VAR_github_app_id: ${{ secrets.APP_ID }} 86 | TF_VAR_github_app_client_id: ${{ secrets.APP_CLIENT_ID }} 87 | TF_VAR_github_enterprise_url: ${{ secrets.ENTERPRISE_SERVER_URL }} 88 | TF_IN_AUTOMATION: true 89 | AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} 90 | AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} 91 | environment: ${{needs.prechecks.outputs.environment}} 92 | 93 | timeout-minutes: 30 94 | 95 | steps: 96 | - name: Acknowledge self hosted runner deprovisioning request 97 | id: acknowledge 98 | uses: actions/github-script@v3 99 | with: 100 | github-token: ${{secrets.GITHUB_TOKEN}} 101 | script: | 102 | const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}` 103 | const { environment } = process.env; 104 | pr = await github.pulls.get( 105 | { 106 | ...context.repo, 107 | pull_number: context.issue.number 108 | } 109 | ) 110 | 111 | if (pr.status !== 200) { 112 | message = 'Could not retrieve PR info: ${permissionRes.status}' 113 | core.setOutput('error', message) 114 | throw new Error(message) 115 | } 116 | 117 | if (!pr.data.mergeable) { 118 | message = 'This PR is currently not mergeable, please update it with the latest changes from the target branch.' 119 | core.setOutput('error', message) 120 | throw new Error(message) 121 | } 122 | 123 | github.issues.createComment({ 124 | ...context.repo, 125 | issue_number: context.issue.number, 126 | body: '👋 @' + context.actor + ' deprovisioning self hosted runner scale set environment ' + environment + ' now, you can watch the progress [here](' + log_url + ') ...' 127 | }) 128 | 129 | core.setOutput('ref', pr.data.head.ref) 130 | 131 | - name: Checkout 132 | uses: actions/checkout@v2 133 | with: 134 | ref: ${{ steps.acknowledge.outputs.ref }} 135 | 136 | - name: Setup node 137 | uses: actions/setup-node@v2.1.2 138 | with: 139 | node-version: '12' 140 | 141 | - name: HashiCorp - Setup Terraform 142 | uses: hashicorp/setup-terraform@v1.3.2 143 | with: 144 | terraform_version: 0.14.5 145 | terraform_wrapper: true 146 | 147 | - name: Terraform Init AWS environment 148 | id: init-aws 149 | run: cd actions-runner-envs-archived/$environment && terraform init 150 | 151 | - name: Terraform Apply AWS environment changes 152 | id: apply-changes 153 | run: | 154 | cd actions-runner-envs-archived/$environment 155 | terraform destroy -no-color -input=false -auto-approve 156 | continue-on-error: false 157 | 158 | - name: Deprovisioning Succeeded 159 | id: deprovisioning-succeeded 160 | if: success() 161 | uses: actions/github-script@v3 162 | env: 163 | CONFIG_APPLIED: ${{ steps.apply-changes.outputs.stdout }} 164 | with: 165 | github-token: ${{ secrets.GITHUB_TOKEN }} 166 | script: | 167 | const commentBody = `\ 168 | ### :computer: Self-hosted runner configuration deleted successfully :runner: 169 | 170 |
171 | 🚀 Configuration removed 172 | 173 | \`\`\` 174 | ${process.env.CONFIG_APPLIED} 175 | \`\`\` 176 |
177 | 178 | Merging this PR now ... 179 | To create a new scale set configuration, open another issue. 180 | `; 181 | 182 | await github.issues.createComment({ 183 | ...context.repo, 184 | issue_number: ${{ github.event.issue.number }}, 185 | body: commentBody 186 | }) 187 | 188 | github.pulls.merge({ 189 | ...context.repo, 190 | pull_number: context.issue.number 191 | }); 192 | 193 | - name: Deprovisioning Failed 194 | id: deprovisioning-failed 195 | if: failure() 196 | uses: actions/github-script@v3 197 | with: 198 | github-token: ${{ secrets.GITHUB_TOKEN }} 199 | script: | 200 | const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}` 201 | 202 | github.issues.createComment({ 203 | ...context.repo, 204 | issue_number: ${{ github.event.issue.number }}, 205 | body: `Self-hosted runner deprovision request failed :cry:. [View error logs](${ log_url}).` 206 | }) 207 | 208 | # Force an unlock on the Terraform state file if we were cancelled or failed (probably really only needed for cancelled). 209 | - name: Reveal Terraform Lock 210 | id: reveal_terraform_lock 211 | if: cancelled() || failure() 212 | run: cd actions-runner-envs-archived/$environment && terraform force-unlock -force abc 213 | continue-on-error: true 214 | 215 | - name: Extract Terraform Lock ID 216 | if: cancelled() || failure() 217 | id: extract_lock 218 | uses: actions/github-script@v3 219 | env: 220 | TERRAFORM_LOCK_OUTPUT: ${{ steps.reveal_terraform_lock.outputs.stderr }} 221 | with: 222 | lock_data: ${{ steps.reveal_terraform_lock.outputs.stderr }} 223 | script: > 224 | console.log(process.env.TERRAFORM_LOCK_OUTPUT); 225 | console.log(core.getInput('lock_data')); 226 | 227 | const matched = /Lock Info:\s*ID:\s*([a-z|0-9|-]*)/gm.exec(core.getInput('lock_data')); 228 | if (matched) { 229 | core.setOutput('lock_id', matched[1]); 230 | } 231 | core.setOutput('requires_unlock', !!matched); 232 | 233 | - name: Unlock Terraform State 234 | id: unlock_terraform_state 235 | if: (cancelled() || failure()) && steps.extract_lock.outputs.requires_unlock == 'true' 236 | run: cd actions-runner-envs-archived/$environment && terraform force-unlock -force ${{ steps.extract_lock.outputs.lock_id }} 237 | -------------------------------------------------------------------------------- /.github/workflows/create-edit-self-hosted-runner-issue.yml: -------------------------------------------------------------------------------- 1 | name: IssueOps - Create self-hosted runner scale set request 2 | 3 | on: 4 | issues: 5 | types: [opened, reopened] 6 | 7 | jobs: 8 | create_edit_actions_runner_pr: 9 | if: contains(github.event.issue.labels.*.name, 'self-hosted-runner-scale-set') 10 | name: Create self hosted runner creation/update PR 11 | 12 | runs-on: Linux 13 | 14 | steps: 15 | 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Extract Issue Demo Specification 20 | id: issue_body 21 | uses: peter-murray/issue-body-parser-action@v1 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | issue_id: ${{ github.event.issue.number }} 25 | 26 | - name: Validate specification 27 | id: validate_runner_params 28 | uses: actions/github-script@v3 29 | env: 30 | ISSUE_PAYLOAD: ${{ steps.issue_body.outputs.payload }} 31 | with: 32 | github-token: ${{ secrets.GITHUB_TOKEN }} 33 | script: | 34 | const issuePayload = JSON.parse(process.env.ISSUE_PAYLOAD); 35 | 36 | const runner = { 37 | organization: issuePayload.organization, 38 | environment: issuePayload.environment, 39 | maxRunners: issuePayload.maxRunners, 40 | minRunners: issuePayload.minRunners, 41 | runnerGroup: issuePayload.runnerGroup, 42 | amiFilter: issuePayload.amiFilter, 43 | amiOwner: issuePayload.amiOwner 44 | }; 45 | 46 | 47 | if (! /^[a-zA-Z0-9_]+$/.test(runner.environment)) { 48 | const error = `The name of the environment contains illegal characters: ${runner.environment}`; 49 | core.error(error); 50 | core.setFailed(error); 51 | } 52 | 53 | if (! /^[a-zA-Z0-9-_]+$/.test(runner.organization)) { 54 | const error = `The name of the organization contains illegal characters: ${runner.organization}`; 55 | core.error(error); 56 | core.setFailed(error); 57 | } 58 | 59 | if (! /^[0-9]+$/.test(runner.minRunners)) { 60 | const error = `The min runners value has to be a number: ${runner.min_numbers}`; 61 | core.error(error); 62 | core.setFailed(error); 63 | } 64 | 65 | if (! /^[0-9]+$/.test(runner.maxRunners)) { 66 | const error = `The max runners value has to be a number: ${runner.max_numbers}`; 67 | core.error(error); 68 | core.setFailed(error); 69 | } 70 | 71 | if (! /^[a-zA-Z0-9-_]+$/.test(runner.amiOwner)) { 72 | const error = `The AMI owner contains illegal characters: ${runner.ami_owner}`; 73 | core.error(error); 74 | core.setFailed(error); 75 | } 76 | 77 | 78 | if (! /^[a-zA-Z0-9-_\*]+$/.test(runner.amiOwner)) { 79 | const error = `The AMI filter contains illegal characters: ${runner.ami_filter}`; 80 | core.error(error); 81 | core.setFailed(error); 82 | } 83 | 84 | if (! /^[a-zA-Z0-9-_]+$/.test(runner.group)) { 85 | const error = `The name of the runner group contains illegal characters: ${runner.group}`; 86 | core.error(error); 87 | core.setFailed(error); 88 | } 89 | 90 | core.setOutput('runner_environment', runner.environment); 91 | core.setOutput('runner_organization', runner.organization); 92 | core.setOutput('runner_min_runners', runner.minRunners); 93 | core.setOutput('runner_max_runners', runner.maxRunners); 94 | core.setOutput('runner_runner_group', runner.runnerGroup); 95 | core.setOutput('runner_ami_filter', runner.amiFilter); 96 | core.setOutput('runner_ami_owner', runner.amiOwner); 97 | 98 | - name: Create actions-runner environment 99 | id: create_actions-runner-environment 100 | env: 101 | RUNNER_ENVIRONMENT: ${{ steps.validate_runner_params.outputs.runner_environment }} 102 | RUNNER_MIN_REPLICAS: "${{ steps.validate_runner_params.outputs.runner_min_runners }}" 103 | RUNNER_MAX_REPLICAS: "${{ steps.validate_runner_params.outputs.runner_max_runners }}" 104 | RUNNER_ORGANIZATION: ${{ steps.validate_runner_params.outputs.runner_organization }} 105 | RUNNER_AMI_FILTER: ${{ steps.validate_runner_params.outputs.runner_ami_filter }} 106 | RUNNER_AMI_OWNER: ${{ steps.validate_runner_params.outputs.runner_ami_owner }} 107 | RUNNER_GROUP: ${{ steps.validate_runner_params.outputs.runner_runner_group }} 108 | run: | 109 | mkdir -p actions-runner-envs/$RUNNER_ENVIRONMENT 110 | for i in `ls actions-runner-envs/template-env/`; do envsubst < actions-runner-envs/template-env/$i > actions-runner-envs/$RUNNER_ENVIRONMENT/$i; done 111 | 112 | - name: Create PR 113 | id: create_pr 114 | uses: peter-evans/create-pull-request@v3.7.0 115 | env: 116 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 117 | with: 118 | branch: create-edit-actions-runner-${{ steps.validate_runner_params.outputs.runner_environment }} 119 | title: Create or edit self hosted runner scale set for env ${{ steps.validate_runner_params.outputs.runner_environment }} 120 | delete-branch: true 121 | commit-message: "AWS changes for runner scale set in env ${{ steps.validate_runner_params.outputs.runner_environment }}" 122 | body: > 123 | This PR contains the AWS changes needed to create a self hosted runner scale set with the following characteristics: 124 | 125 | * __Organization__: ${{ steps.validate_runner_params.outputs.runner_organization }} 126 | 127 | * __Environment__: ${{ steps.validate_runner_params.outputs.runner_environment }} 128 | 129 | * __MinRunners__: ${{ steps.validate_runner_params.outputs.runner_min_runners }} 130 | 131 | * __MaxRunners__: ${{ steps.validate_runner_params.outputs.runner_max_runners }} 132 | 133 | * __RunnerGroup__: ${{ steps.validate_runner_params.outputs.runner_runner_group }} 134 | 135 | * __AMIFilter__: ${{ steps.validate_runner_params.outputs.runner_ami_filter }} 136 | 137 | * __AMIOwner__: ${{ steps.validate_runner_params.outputs.runner_ami_owner }} 138 | 139 | ![image](https://user-images.githubusercontent.com/1872314/106927306-52228b80-6712-11eb-9cb8-11e91d719b9a.png) 140 | 141 | The PR has been initially created by and closes #${{ github.event.issue.number }} after successful merge. 142 | 143 | If you are ok with the changes and have carefully reviewed them, please provide an `/approve` comment 144 | 145 | - name: Report Success 146 | id: report_success 147 | if: success() 148 | uses: peter-evans/create-or-update-comment@v1 149 | with: 150 | issue-number: ${{ github.event.issue.number }} 151 | token: ${{ secrets.GITHUB_TOKEN }} 152 | body: > 153 | ### :computer: Self hosted runner creation/update request acknowledged :runner: 154 | 155 | 156 | Please review and approve the following details and approve them in [this pull request](${{ steps.create_pr.outputs.pull-request-url }}). 157 | 158 | * __Organization__: ${{ steps.validate_runner_params.outputs.runner_organization }} 159 | 160 | * __Environment__: ${{ steps.validate_runner_params.outputs.runner_environment }} 161 | 162 | * __MinRunners__: ${{ steps.validate_runner_params.outputs.runner_min_runners }} 163 | 164 | * __MaxRunners__: ${{ steps.validate_runner_params.outputs.runner_max_runners }} 165 | 166 | * __RunnerGroup__: ${{ steps.validate_runner_params.outputs.runner_runner_group }} 167 | 168 | * __AMIFilter__: ${{ steps.validate_runner_params.outputs.runner_ami_filter }} 169 | 170 | * __AMIOwner__: ${{ steps.validate_runner_params.outputs.runner_ami_owner }} 171 | 172 | 173 | - name: Report Failure 174 | id: report_failure 175 | if: failure() 176 | uses: actions/github-script@v3 177 | with: 178 | github-token: ${{ secrets.GITHUB_TOKEN }} 179 | script: | 180 | const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}` 181 | 182 | github.issues.createComment({ 183 | ...context.repo, 184 | issue_number: ${{ github.event.issue.number }}, 185 | body: `Self-hosted runner creation/update request failed :cry:. [View error logs](${ log_url}).` 186 | }) 187 | -------------------------------------------------------------------------------- /.github/workflows/delete-self-hosted-runner-issue.yml: -------------------------------------------------------------------------------- 1 | name: IssueOps - Delete self-hosted runner scale set request 2 | 3 | on: 4 | issues: 5 | types: [opened, reopened] 6 | 7 | jobs: 8 | delete_archive_actions_runner_pr: 9 | if: contains(github.event.issue.labels.*.name, 'delete-self-hosted-runner-scale-set') 10 | name: Create self hosted runner deletion/archival PR 11 | 12 | runs-on: Linux 13 | 14 | steps: 15 | 16 | - name: Checkout 17 | uses: actions/checkout@v2 18 | 19 | - name: Extract Issue Demo Specification 20 | id: issue_body 21 | uses: peter-murray/issue-body-parser-action@v1 22 | with: 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | issue_id: ${{ github.event.issue.number }} 25 | 26 | - name: Validate specification 27 | id: validate_runner_params 28 | uses: actions/github-script@v3 29 | env: 30 | ISSUE_PAYLOAD: ${{ steps.issue_body.outputs.payload }} 31 | with: 32 | github-token: ${{ secrets.GITHUB_TOKEN }} 33 | script: | 34 | const issuePayload = JSON.parse(process.env.ISSUE_PAYLOAD); 35 | 36 | const runner = { 37 | environment: issuePayload.environment, 38 | }; 39 | 40 | if (! /^[a-zA-Z0-9_]+$/.test(runner.environment)) { 41 | const error = `The name of the environment contains illegal characters: ${runner.environment}`; 42 | core.error(error); 43 | core.setFailed(error); 44 | } 45 | 46 | core.setOutput('runner_environment', runner.environment); 47 | 48 | - name: Delete and archive actions-runner environment 49 | id: create_actions-runner-environment 50 | env: 51 | RUNNER_ENVIRONMENT: ${{ steps.validate_runner_params.outputs.runner_environment }} 52 | run: | 53 | mkdir -p actions-runner-envs-archived/$RUNNER_ENVIRONMENT 54 | rm -rf actions-runner-envs-archived/$RUNNER_ENVIRONMENT/* 55 | mv actions-runner-envs/$RUNNER_ENVIRONMENT actions-runner-envs-archived/ 56 | 57 | - name: Create PR 58 | id: create_pr 59 | uses: peter-evans/create-pull-request@v3.7.0 60 | env: 61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | with: 63 | branch: delete-archive-actions-runner-${{ steps.validate_runner_params.outputs.runner_environment }} 64 | title: Delete and archive self hosted runner scale set for env ${{ steps.validate_runner_params.outputs.runner_environment }} 65 | delete-branch: true 66 | commit-message: "Archive AWS changes for runner scale set in env ${{ steps.validate_runner_params.outputs.runner_environment }}" 67 | body: > 68 | This PR contains the AWS changes needed to delete the self hosted runner scale set associated with environment ${{ steps.validate_runner_params.outputs.runner_environment }} 69 | 70 | The PR has been initially created by and closes #${{ github.event.issue.number }} after successful merge. 71 | 72 | If you are ok with the changes and have carefully reviewed them, please provide an `/approve` comment 73 | 74 | - name: Report Success 75 | id: report_success 76 | if: success() 77 | uses: peter-evans/create-or-update-comment@v1 78 | with: 79 | issue-number: ${{ github.event.issue.number }} 80 | token: ${{ secrets.GITHUB_TOKEN }} 81 | body: > 82 | ### :computer: Self hosted runner deletion/archival request acknowledged :runner: 83 | 84 | 85 | Please review and approve the following details and approve them in [this pull request](${{ steps.create_pr.outputs.pull-request-url }}). 86 | 87 | * __Environment__: ${{ steps.validate_runner_params.outputs.runner_environment }} 88 | 89 | - name: Report Failure 90 | id: report_failure 91 | if: failure() 92 | uses: actions/github-script@v3 93 | with: 94 | github-token: ${{ secrets.GITHUB_TOKEN }} 95 | script: | 96 | const log_url = `${process.env.GITHUB_SERVER_URL}/${context.repo.owner}/${context.repo.repo}/actions/runs/${process.env.GITHUB_RUN_ID}` 97 | 98 | github.issues.createComment({ 99 | ...context.repo, 100 | issue_number: ${{ github.event.issue.number }}, 101 | body: `Self-hosted runner deletion/archival request failed :cry:. [View error logs](${ log_url}).` 102 | }) 103 | -------------------------------------------------------------------------------- /.github/workflows/test-runners-example.yaml: -------------------------------------------------------------------------------- 1 | name: Scaling test for runner env example 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | numberJobs: 7 | description: 'Number of parallel jobs (should stay below 100)' 8 | required: true 9 | default: '10' 10 | 11 | jobs: 12 | generate-matrix: 13 | name: "Generate jobs for example" 14 | outputs: 15 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 16 | runs-on: [ Linux ] 17 | steps: 18 | - name: generate-matrix 19 | id: generate-matrix 20 | run: echo "::set-output name=matrix::[`seq -s , ${{ github.event.inputs.numberJobs }}`]" 21 | 22 | stress: 23 | name: "Stress tests for example" 24 | runs-on: [ aws, example ] 25 | needs: [generate-matrix] 26 | 27 | strategy: 28 | matrix: 29 | job-number: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 30 | 31 | steps: 32 | - name: Stress test ${{ matrix.job-number }} of env example 33 | run: echo "Matrix version ${{ matrix.job-number }}" && sleep $((RANDOM%120+60)) 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # state 2 | *.tfstate* 3 | 4 | actions-runner/** 5 | 6 | # Module directory 7 | .terraform/ 8 | *.hcl 9 | 10 | # keys 11 | *id_rsa* 12 | 13 | # other 14 | .idea 15 | .DS_Store 16 | *.out 17 | example/*.secrets*.tfvars 18 | .envrc 19 | *.zip 20 | *.gz 21 | *.tgz 22 | *.env 23 | .vscode 24 | *.zip 25 | 26 | 27 | **/coverage/* 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # setup-self-hosted-runner-aws-ec2 2 | 3 | [![awesome-runners](https://img.shields.io/badge/listed%20on-awesome--runners-blue.svg)](https://github.com/jonico/awesome-runners) 4 | 5 | IssueOps example on how to set up an autoscaling, self-hosted runner fleet in AWS using [philips-labs/terraform-aws-github-runner](https://github.com/philips-labs/terraform-aws-github-runner) 6 | 7 | ![Architecture](docs/component-overview.svg) 8 | 9 | In order to setup or modify an auto-scaling runner set, just [create an issue](https://octodemo.com/baseline/setup-self-hosted-runner-aws-ec2/issues/new/choose): 10 | 11 | ![image](https://user-images.githubusercontent.com/1872314/106957297-20bcb680-6738-11eb-8c79-2b813939de43.png) 12 | 13 | ... /approve the generated Terraform plan in the resulting pull request and follow the instructions shown in the PR comment: 14 | 15 | ![image](https://user-images.githubusercontent.com/1872314/106965075-65018400-6743-11eb-9849-98d4a3ffd182.png) 16 | 17 | The PR comment will also contain an example GitHub Actions workflow to test the scaling of your newly created setup: 18 | 19 | ![image](https://user-images.githubusercontent.com/1872314/106965278-ac881000-6743-11eb-9fc9-04477cb6dc03.png) 20 | 21 | ### Using custom images 22 | 23 | The amiFilter and amiOwner fields can be used to specify an alternative runner image. Please use a filter that matches an AMI available in your region. An example for a custom AMI can be found [here](custom-images/actions-runner-with-additional-packages.json). You can create a new image using packer like this: 24 | 25 | ```bash 26 | packer build -var "aws_access_key=$AWS_ACCESS_KEY" -var "aws_secret_key=$AWS_SECRET_KEY" custom-images/actions-runner-with-additional-packages.json 27 | ``` 28 | 29 | # Setting up a copy of this IssueOps based repository 30 | 31 | If you like to work directly with this repository, there is no further need but to follow the comments in the issues created by the template and the approved PR. 32 | 33 | If you like to setup a copy of this repository somewhere else, you would need to set the following GitHub Action credentials: 34 | 35 | * APP_CLIENT_SECRET: GitHub App client secret 36 | * APP_KEY_BASE64: GitHub App private key, base64 encoded 37 | * APP_ID: GitHub App Id 38 | * APP_CLIENT_ID: GitHub App Client ID 39 | * ENTERPRISE_SERVER_URL: GitHub Enterprise Server URL - leave blank if you are using GitHub.com 40 | * AWS_ACCESS_KEY_ID: AWS access key id used to apply Terraform plans 41 | * AWS_SECRET_ACCESS_KEY: AWS secret access key used to apply the Terraform plans 42 | 43 | Furthermore, you would have to adopt [the Terraform state backend configuration](actions-runner-envs/default/backend.tf) to an S3 bucket and DynamoDB table of your choice. A Terraform configuration that shows how to create your own S3 bucket and table can be found [here](bootstrap-terraform-state-bucket/). 44 | 45 | You also need to create a GitHub App as described in the next section. 46 | 47 | ### Needed issue labels 48 | 49 | Last but not least, you would need to create two labels in the copy of this repository: `self-hosted-runner-scale-set` and `delete-self-hosted-runner-scale-set` 50 | 51 | ### How to create the GitHub App and architecture motivation 52 | 53 | The rest of this README has been copied from the [philips-labs/terraform-aws-github-runner](https://github.com/philips-labs/terraform-aws-github-runner) project to provide additional information on the setup that is created and how to create the needed GitHub App if you like to start from scratch. 54 | 55 | # Terraform module for scalable self hosted GitHub action runners 56 | 57 | This [Terraform](https://www.terraform.io/) module creates the required infrastructure needed to host [GitHub Actions](https://github.com/features/actions) self hosted, auto scaling runners on [AWS spot instances](https://aws.amazon.com/ec2/spot/). It provides the required logic to handle the life cycle for scaling up and down using a set of AWS Lambda functions. Runners are scaled down to zero to avoid costs when no workflows are active. 58 | 59 | ## Motivation 60 | 61 | GitHub Actions `self hosted` runners provide a flexible option to run CI workloads on infrastructure of your choice. Currently there is no option provided to automate the creation and scaling of action runners. This module takes care of creating the AWS infrastructure to host action runners on spot instances. It provides lambda modules to orchestrate the life cycle of the action runners. 62 | 63 | Lambda is chosen as runtime for two major reasons. First it allows to create small components with minimal access to AWS and GitHub. Secondly it provides a scalable setup with minimal costs that works on repo level and scales to organization level. The lambdas will create Linux based EC2 instances with Docker to serve CI workloads that can run on Linux and/or Docker. The main goal is to support Docker based workloads. 64 | 65 | A logical question would be why not Kubernetes? In the current approach we stay close to the way the GitHub action runners are available today. The approach is to install the runner on a host where the required software is available. With this setup we stay quite close to the current GitHub approach. Another logical choice would be AWS Auto Scaling groups. This choice would typically require much more permissions on instance level to GitHub. And besides that, scaling up and down is not trivial. 66 | 67 | ## Overview 68 | 69 | The moment a GitHub action workflow requiring a `self-hosted` runner is triggered, GitHub will try to find a runner which can execute the workload. This module reacts to GitHub's [`check_run` event](https://docs.github.com/en/free-pro-team@latest/developers/webhooks-and-events/webhook-events-and-payloads#check_run) for the triggered workflow and creates a new runner if necessary. 70 | 71 | For receiving the `check_run` event, a GitHub App needs to be created with a webhook to which the event will be published. Installing the GitHub App in a specific repository or all repositories ensures the `check_run` event will be sent to the webhook. 72 | 73 | In AWS a [API gateway](https://docs.aws.amazon.com/apigateway/index.html) endpoint is created that is able to receive the GitHub webhook events via HTTP post. The gateway triggers the webhook lambda which will verify the signature of the event. This check guarantees the event is sent by the GitHub App. The lambda only handles `check_run` events with status `created`. The accepted events are posted on a SQS queue. Messages on this queue will be delayed for a configurable amount of seconds (default 30 seconds) to give the available runners time to pick up this build. 74 | 75 | The "scale up runner" lambda is listening to the SQS queue and picks up events. The lambda runs various checks to decide whether a new EC2 spot instance needs to be created. For example, the instance is not created if the build is already started by an existing runner, or the maximum number of runners is reached. 76 | 77 | The Lambda first requests a registration token from GitHub which is needed later by the runner to register itself. This avoids that the EC2 instance, which later in the process will install the agent, needs administration permissions to register the runner. Next the EC2 spot instance is created via the launch template. The launch template defines the specifications of the required instance and contains a [`user_data`](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) script. This script will install the required software and configure it. The registration token for the action runner is stored in the parameter store (SSM) from which the user data script will fetch it and delete it once it has been retrieved. Once the user data script is finished the action runner should be online and the workflow will start in seconds. 78 | 79 | Scaling down the runners is at the moment brute-forced, every configurable amount of minutes a lambda will check every runner (instance) if it is busy. In case the runner is not busy it will be removed from GitHub and the instance terminated in AWS. At the moment there seems no other option to scale down more smoothly. 80 | 81 | Downloading the GitHub Action Runner distribution can be occasionally slow (more than 10 minutes). Therefore a lambda is introduced that synchronizes the action runner binary from GitHub to an S3 bucket. The EC2 instance will fetch the distribution from the S3 bucket instead of the internet. 82 | 83 | Secrets and private keys which are passed to the lambdas as environment variables are encrypted by default by a KMS key managed by the module. Alternatively you can pass your own KMS key. Encryption via KMS can be complete disabled by setting `encrypt_secrets` to `false`. 84 | 85 | ![Architecture](docs/component-overview.svg) 86 | 87 | Permission are managed on several places. Below the most important ones. For details check the Terraform sources. 88 | 89 | - The GitHub App requires access to actions and publish `check_run` events to AWS. 90 | - The scale up lambda should have access to EC2 for creating and tagging instances. 91 | - The scale down lambda should have access to EC2 to terminate instances. 92 | 93 | Besides these permissions, the lambdas also need permission to CloudWatch (for logging and scheduling), SSM and S3. 94 | 95 | ## Usages 96 | 97 | The module supports two main scenarios for creating runners. On repository level a runner will be dedicated to only one repository, no other repository can use the runner. On organization level you can use the runner(s) for all the repositories within the organization. See https://help.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners for more information. The IssueOps part of this project so far only exposes the org runner aspect of the underlying Terraform module. 98 | 99 | GitHub workflows fail immediately if there is no action runner available for your builds. Since this module supports scaling down to zero, builds will fail in case there is no active runner available. We recommend to create an offline runner with matching labels to the configuration. Create this runner manually by following the GitHub instructions for adding a new runner on your local machine. If you stop the process after the step of running the `config.sh` script the runner will remain offline. This offline runner ensures that builds will not fail immediately and stay queued until there is an EC2 runner to pick it up. 100 | 101 | The setup consists of running Terraform to create all AWS resources and manually configuring the GitHub App. The Terraform module requires configuration from the GitHub App and the GitHub app requires output from Terraform. Therefore you first create the GitHub App and configure the basics, then run Terraform (via IssueOps), and afterwards finalize the configuration of the GitHub App. 102 | 103 | ### Setup GitHub App (part 1 - only needed if you like to start from scratch) 104 | 105 | Go to GitHub and create a new app. Beware you can create apps your organization or for a user. For now we support only organization level apps. 106 | 107 | 1. Create app in Github 108 | 2. Choose a name 109 | 3. Choose a website (mandatory, not required for the module). 110 | 4. Disable the webhook for now (we will configure this later). 111 | 5. Permissions for all runners: 112 | - Repository: 113 | - `Actions`: Read-only (check for queued jobs) 114 | - `Checks`: Read-only (receive events for new builds) 115 | - `Metadata`: Read-only (default/required) 116 | 6. _Permissions for repo level runners only_: 117 | - Repository: 118 | - `Administration`: Read & write (to register runner) 119 | 7. _Permissions for organization level runners only_: 120 | - Organization 121 | - `Administration`: Read & write (to register runner) 122 | - `Self-hosted runners`: Read & write (to register runner) 123 | 8. Save the new app. 124 | 9. On the General page, make a note of the "App ID" and "Client ID" parameters. 125 | 10. Create a new client secret and also write it down. 126 | 11. Generate a new private key and save the `app.private-key.pem` file. 127 | 128 | ### Setup GitHub App (part 2 - only needed if you start from scratch) 129 | 130 | Go back to the GitHub App and update the following settings. 131 | 132 | 1. Enable the webhook. 133 | 2. Provide the webhook url, should be part of the output of the IssueOps generated pull request you /approve. 134 | 3. Provide the webhook secret. 135 | 4. Enable the `check_run` event for the webhook. 136 | 5. In the "Install App" section, install the App in your organization, either in all or in selected repositories. 137 | 138 | You are now ready to run action workloads on self hosted runner. Remember that builds will fail if there is no (offline) runner available with matching labels. 139 | 140 | 141 | ## Debugging 142 | 143 | In case the setup does not work as intended follow the trace of events: 144 | 145 | - In the GitHub App configuration, the Advanced page displays all webhook events that were sent. 146 | - In AWS CloudWatch, every lambda has a log group. Look at the logs of the `webhook` and `scale-up` lambdas. 147 | - In AWS SQS you can see messages available or in flight. 148 | - Once an EC2 instance is running, you can connect to it in the EC2 user interface using Session Manager. Check the user data script using `cat /var/log/user-data.log`. By default several log files of the instances are streamed to AWS CloudWatch, look for a log group named `/runners`. In the log group you should see at least the log streams for the user data installation and runner agent. 149 | - Registered instances should show up in the Settings - Actions page of the repository or organization (depending on the installation mode). 150 | 151 | 152 | ## Inputs 153 | 154 | The IssueOps template only exposes a couple of configuration parameters, you can fine tune man< more if you manually edit the generated Terraform PR: 155 | 156 | | Name | Description | Type | Default | Required | 157 | |------|-------------|------|---------|:--------:| 158 | | ami\_filter | List of maps used to create the AMI filter for the action runner AMI. By default amazon linux 2 is used. | `map(list(string))` | `{}` | no | 159 | | ami\_owners | The list of owners used to select the AMI of action runner instances. | `list(string)` |
[
"amazon"
]
| no | 160 | | aws\_region | AWS region. | `string` | n/a | yes | 161 | | block\_device\_mappings | The EC2 instance block device configuration. Takes the following keys: `device_name`, `delete_on_termination`, `volume_type`, `volume_size`, `encrypted`, `iops` | `map(string)` | `{}` | no | 162 | | cloudwatch\_config | (optional) Replaces the module default cloudwatch log config. See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html for details. | `string` | `null` | no | 163 | | create\_service\_linked\_role\_spot | (optional) create the serviced linked role for spot instances that is required by the scale-up lambda. | `bool` | `false` | no | 164 | | enable\_cloudwatch\_agent | Enabling the cloudwatch agent on the ec2 runner instances, the runner contains default config. Configuration can be overridden via `cloudwatch_config`. | `bool` | `true` | no | 165 | | enable\_organization\_runners | n/a | `bool` | n/a | yes | 166 | | enable\_ssm\_on\_runners | Enable to allow access the runner instances for debugging purposes via SSM. Note that this adds additional permissions to the runner instances. | `bool` | `false` | no | 167 | | encrypt\_secrets | Encrypt secret variables for lambda's such as secrets and private keys. | `bool` | `true` | no | 168 | | environment | A name that identifies the environment, used as prefix and for tagging. | `string` | n/a | yes | 169 | | ghes\_url | GitHub Enterprise Server URL. Example: https://github.internal.co - DO NOT SET IF USING PUBLIC GITHUB | `string` | `null` | no | 170 | | github\_app | GitHub app parameters, see your github app. Ensure the key is the base64-encoded `.pem` file (the output of `base64 app.private-key.pem`, not the content of `private-key.pem`). |
object({
key_base64 = string
id = string
client_id = string
client_secret = string
webhook_secret = string
})
| n/a | yes | 171 | | idle\_config | List of time period that can be defined as cron expression to keep a minimum amount of runners active instead of scaling down to 0. By defining this list you can ensure that in time periods that match the cron expression within 5 seconds a runner is kept idle. |
list(object({
cron = string
timeZone = string
idleCount = number
}))
| `[]` | no | 172 | | instance\_profile\_path | The path that will be added to the instance\_profile, if not set the environment name will be used. | `string` | `null` | no | 173 | | instance\_type | Instance type for the action runner. | `string` | `"m5.large"` | no | 174 | | key\_name | Key pair name | `string` | `null` | no | 175 | | kms\_key\_id | Custom KMS key to encrypted lambda secrets, if not provided and `encrypt_secrets` = `true` a KMS key will be created by the module. Secrets will be encrypted with a context `Environment = var.environment`. | `string` | `null` | no | 176 | | lambda\_s3\_bucket | S3 bucket from which to specify lambda functions. This is an alternative to providing local files directly. | `any` | `null` | no | 177 | | lambda\_security\_group\_ids | List of subnets in which the action runners will be launched, the subnets needs to be subnets in the `vpc_id`. | `list(string)` | `[]` | no | 178 | | lambda\_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)` | `[]` | no | 179 | | logging\_retention\_in\_days | Specifies the number of days you want to retain log events for the lambda log group. Possible values are: 0, 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, and 3653. | `number` | `180` | no | 180 | | manage\_kms\_key | Let the module manage the KMS key. | `bool` | `true` | no | 181 | | minimum\_running\_time\_in\_minutes | The time an ec2 action runner should be running at minimum before terminated if non busy. | `number` | `5` | no | 182 | | role\_path | The path that will be added to role path for created roles, if not set the environment name will be used. | `string` | `null` | no | 183 | | role\_permissions\_boundary | Permissions boundary that will be added to the created roles. | `string` | `null` | no | 184 | | runner\_additional\_security\_group\_ids | (optional) List of additional security groups IDs to apply to the runner | `list(string)` | `[]` | no | 185 | | runner\_allow\_prerelease\_binaries | Allow the runners to update to prerelease binaries. | `bool` | `false` | no | 186 | | runner\_as\_root | Run the action runner under the root user. | `bool` | `false` | no | 187 | | runner\_binaries\_syncer\_lambda\_timeout | Time out of the binaries sync lambda in seconds. | `number` | `300` | no | 188 | | runner\_binaries\_syncer\_lambda\_zip | File location of the binaries sync lambda zip file. | `string` | `null` | no | 189 | | runner\_extra\_labels | Extra labels for the runners (GitHub). Separate each label by a comma | `string` | `""` | no | 190 | | runner\_group\_name | Name of the runner group. | `string` | `"Default"` | no | 191 | | runner\_iam\_role\_managed\_policy\_arns | Attach AWS or customer-managed IAM policies (by ARN) to the runner IAM role | `list(string)` | `[]` | no | 192 | | runner\_log\_files | (optional) Replaces the module default cloudwatch log config. See https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch-Agent-Configuration-File-Details.html for details. |
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 | 193 | | runners\_lambda\_s3\_key | S3 key for runners lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no | 194 | | runners\_lambda\_s3\_object\_version | S3 object version for runners lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no | 195 | | runners\_lambda\_zip | File location of the lambda zip file for scaling runners. | `string` | `null` | no | 196 | | runners\_maximum\_count | The maximum number of runners that will be created. | `number` | `3` | no | 197 | | runners\_scale\_down\_lambda\_timeout | Time out for the scale down lambda in seconds. | `number` | `60` | no | 198 | | runners\_scale\_up\_lambda\_timeout | Time out for the scale up lambda in seconds. | `number` | `180` | no | 199 | | scale\_down\_schedule\_expression | Scheduler expression to check every x for scale down. | `string` | `"cron(*/5 * * * ? *)"` | no | 200 | | 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 | 201 | | syncer\_lambda\_s3\_key | S3 key for syncer lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no | 202 | | syncer\_lambda\_s3\_object\_version | S3 object version for syncer lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no | 203 | | tags | Map of tags that will be added to created resources. By default resources will be tagged with name and environment. | `map(string)` | `{}` | no | 204 | | userdata\_post\_install | Script to be ran after the GitHub Actions runner is installed on the EC2 instances | `string` | `""` | no | 205 | | userdata\_pre\_install | Script to be ran before the GitHub Actions runner is installed on the EC2 instances | `string` | `""` | no | 206 | | 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 | 207 | | vpc\_id | The VPC for security groups of the action runners. | `string` | n/a | yes | 208 | | webhook\_lambda\_s3\_key | S3 key for webhook lambda function. Required if using S3 bucket to specify lambdas. | `any` | `null` | no | 209 | | webhook\_lambda\_s3\_object\_version | S3 object version for webhook lambda function. Useful if S3 versioning is enabled on source bucket. | `any` | `null` | no | 210 | | webhook\_lambda\_timeout | Time out of the webhook lambda in seconds. | `number` | `10` | no | 211 | | webhook\_lambda\_zip | File location of the webhook lambda zip file. | `string` | `null` | no | 212 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "terraform-up-and-running-state-jonico" 4 | key = "aws-github-runner-link/terraform.tfstate" 5 | region = "us-east-2" 6 | dynamodb_table = "terraform-up-and-running-locks-jonico" 7 | encrypt = true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/main.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | environment = var.runners_environment 4 | aws_region = "eu-west-1" 5 | } 6 | 7 | resource "random_password" "random" { 8 | length = 28 9 | } 10 | 11 | module "runners" { 12 | source = "philips-labs/github-runner/aws" 13 | version = "0.10.0" 14 | 15 | aws_region = local.aws_region 16 | vpc_id = module.vpc.vpc_id 17 | subnet_ids = module.vpc.private_subnets 18 | 19 | environment = local.environment 20 | tags = { 21 | Project = "octodemo-aws-github-runner-link" 22 | } 23 | 24 | github_app = { 25 | key_base64 = var.github_app_key_base64 26 | id = var.github_app_id 27 | client_id = var.github_app_client_id 28 | client_secret = var.github_app_client_secret 29 | webhook_secret = random_password.random.result 30 | } 31 | 32 | webhook_lambda_zip = "../download-lambdas/webhook.zip" 33 | runner_binaries_syncer_lambda_zip = "../download-lambdas/runner-binaries-syncer.zip" 34 | runners_lambda_zip = "../download-lambdas/runners.zip" 35 | 36 | enable_organization_runners = true 37 | runner_extra_labels = "auto-scale,aws,link" 38 | runner_group_name = var.runners_group 39 | 40 | # enable access to the runners via SSM 41 | enable_ssm_on_runners = true 42 | 43 | idle_config = [{ 44 | cron = "* * * * * *" 45 | timeZone = "Europe/Amsterdam" 46 | idleCount = var.runners_minimum_count 47 | }] 48 | 49 | # disable KMS and encryption 50 | # encrypt_secrets = false 51 | 52 | # Let the module manage the service linked role 53 | create_service_linked_role_spot = true 54 | 55 | # use GitHub Enterprise url 56 | ghes_url = var.github_enterprise_url 57 | 58 | instance_type = var.instance_type 59 | 60 | minimum_running_time_in_minutes = var.minimum_running_time_in_minutes 61 | 62 | runners_maximum_count = var.runners_maximum_count 63 | 64 | ami_owners = [var.runners_ami_owner] 65 | 66 | ami_filter = { 67 | name = [var.runners_ami_filter] 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/outputs.tf: -------------------------------------------------------------------------------- 1 | output "runners" { 2 | value = { 3 | lambda_syncer_name = module.runners.binaries_syncer.lambda.function_name 4 | } 5 | } 6 | 7 | output "webhook" { 8 | value = { 9 | secret = random_password.random.result 10 | endpoint = module.runners.webhook.endpoint 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.aws_region 3 | } 4 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/terraform.tfvars: -------------------------------------------------------------------------------- 1 | instance_type="t3.micro" 2 | minimum_running_time_in_minutes=10 3 | runners_maximum_count=3 4 | runners_minimum_count=1 5 | runners_environment="link" 6 | runners_ami_owner="869946923544" 7 | runners_ami_filter="actions-runner-with-additional-packages-*" 8 | runners_group="default" 9 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/test-auto-scaling-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Scaling test for runner env link 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | numberJobs: 7 | description: 'Number of parallel jobs (should stay below 100)' 8 | required: true 9 | default: '3' 10 | 11 | jobs: 12 | generate-matrix: 13 | name: "Generate jobs for link" 14 | outputs: 15 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 16 | runs-on: [ self-hosted ] 17 | steps: 18 | - name: generate-matrix 19 | id: generate-matrix 20 | run: echo "::set-output name=matrix::[`seq -s , ${{ github.event.inputs.numberJobs }}`]" 21 | 22 | stress: 23 | name: "Stress tests for link" 24 | runs-on: [ aws, link ] 25 | needs: [generate-matrix] 26 | 27 | strategy: 28 | matrix: 29 | job-number: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 30 | 31 | steps: 32 | - name: Stress test ${{ matrix.job-number }} of env link 33 | run: echo "Matrix version ${{ matrix.job-number }}" && sleep $((RANDOM%120+60)) 34 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "github_app_key_base64" { 3 | sensitive = true 4 | } 5 | 6 | variable "github_app_id" { 7 | } 8 | 9 | variable "github_app_client_id" { 10 | } 11 | 12 | variable "github_app_client_secret" { 13 | sensitive = true 14 | } 15 | 16 | variable "github_enterprise_url" { 17 | default = "" 18 | } 19 | 20 | 21 | variable "instance_type" { 22 | default="t3.micro" 23 | } 24 | 25 | variable "minimum_running_time_in_minutes" { 26 | } 27 | 28 | variable "runners_maximum_count" { 29 | } 30 | 31 | variable "runners_minimum_count" { 32 | } 33 | 34 | variable "runners_ami_owner" { 35 | } 36 | 37 | variable "runners_ami_filter" { 38 | } 39 | 40 | variable "runners_environment" { 41 | } 42 | 43 | variable "runners_group" { 44 | default = "default" 45 | } 46 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/link/vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "git::https://github.com/jonico/terraform-aws-vpc.git?ref=2.1.1" 3 | 4 | environment = local.environment 5 | aws_region = local.aws_region 6 | create_private_hosted_zone = false 7 | } 8 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "terraform-up-and-running-state-jonico" 4 | key = "aws-github-runner-rex/terraform.tfstate" 5 | region = "us-east-2" 6 | dynamodb_table = "terraform-up-and-running-locks-jonico" 7 | encrypt = true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/main.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | environment = var.runners_environment 4 | aws_region = "eu-west-1" 5 | } 6 | 7 | resource "random_password" "random" { 8 | length = 28 9 | } 10 | 11 | module "runners" { 12 | source = "philips-labs/github-runner/aws" 13 | version = "0.10.0" 14 | 15 | aws_region = local.aws_region 16 | vpc_id = module.vpc.vpc_id 17 | subnet_ids = module.vpc.private_subnets 18 | 19 | environment = local.environment 20 | tags = { 21 | Project = "octodemo-aws-github-runner-rex" 22 | } 23 | 24 | github_app = { 25 | key_base64 = var.github_app_key_base64 26 | id = var.github_app_id 27 | client_id = var.github_app_client_id 28 | client_secret = var.github_app_client_secret 29 | webhook_secret = random_password.random.result 30 | } 31 | 32 | webhook_lambda_zip = "../download-lambdas/webhook.zip" 33 | runner_binaries_syncer_lambda_zip = "../download-lambdas/runner-binaries-syncer.zip" 34 | runners_lambda_zip = "../download-lambdas/runners.zip" 35 | 36 | enable_organization_runners = true 37 | runner_extra_labels = "auto-scale,aws,rex" 38 | runner_group_name = var.runners_group 39 | 40 | # enable access to the runners via SSM 41 | enable_ssm_on_runners = true 42 | 43 | idle_config = [{ 44 | cron = "* * * * * *" 45 | timeZone = "Europe/Amsterdam" 46 | idleCount = var.runners_minimum_count 47 | }] 48 | 49 | # disable KMS and encryption 50 | # encrypt_secrets = false 51 | 52 | # Let the module manage the service linked role 53 | create_service_linked_role_spot = true 54 | 55 | # use GitHub Enterprise url 56 | ghes_url = var.github_enterprise_url 57 | 58 | instance_type = var.instance_type 59 | 60 | minimum_running_time_in_minutes = var.minimum_running_time_in_minutes 61 | 62 | runners_maximum_count = var.runners_maximum_count 63 | 64 | ami_owners = [var.runners_ami_owner] 65 | 66 | ami_filter = { 67 | name = [var.runners_ami_filter] 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/outputs.tf: -------------------------------------------------------------------------------- 1 | output "runners" { 2 | value = { 3 | lambda_syncer_name = module.runners.binaries_syncer.lambda.function_name 4 | } 5 | } 6 | 7 | output "webhook" { 8 | value = { 9 | secret = random_password.random.result 10 | endpoint = module.runners.webhook.endpoint 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.aws_region 3 | } 4 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/terraform.tfvars: -------------------------------------------------------------------------------- 1 | instance_type="t3.micro" 2 | minimum_running_time_in_minutes=10 3 | runners_maximum_count=6 4 | runners_minimum_count=0 5 | runners_environment="rex" 6 | runners_ami_owner="869946923544" 7 | runners_ami_filter="actions-runner-with-additional-packages-*" 8 | runners_group="default" 9 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/test-auto-scaling-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Scaling test for runner env rex 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | numberJobs: 7 | description: 'Number of parallel jobs (should stay below 100)' 8 | required: true 9 | default: '6' 10 | 11 | jobs: 12 | generate-matrix: 13 | name: "Generate jobs for rex" 14 | outputs: 15 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 16 | runs-on: [ self-hosted ] 17 | steps: 18 | - name: generate-matrix 19 | id: generate-matrix 20 | run: echo "::set-output name=matrix::[`seq -s , ${{ github.event.inputs.numberJobs }}`]" 21 | 22 | stress: 23 | name: "Stress tests for rex" 24 | runs-on: [ aws, rex ] 25 | needs: [generate-matrix] 26 | 27 | strategy: 28 | matrix: 29 | job-number: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 30 | 31 | steps: 32 | - name: Stress test ${{ matrix.job-number }} of env rex 33 | run: echo "Matrix version ${{ matrix.job-number }}" && sleep $((RANDOM%120+60)) 34 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "github_app_key_base64" { 3 | sensitive = true 4 | } 5 | 6 | variable "github_app_id" { 7 | } 8 | 9 | variable "github_app_client_id" { 10 | } 11 | 12 | variable "github_app_client_secret" { 13 | sensitive = true 14 | } 15 | 16 | variable "github_enterprise_url" { 17 | default = "" 18 | } 19 | 20 | 21 | variable "instance_type" { 22 | default="t3.micro" 23 | } 24 | 25 | variable "minimum_running_time_in_minutes" { 26 | } 27 | 28 | variable "runners_maximum_count" { 29 | } 30 | 31 | variable "runners_minimum_count" { 32 | } 33 | 34 | variable "runners_ami_owner" { 35 | } 36 | 37 | variable "runners_ami_filter" { 38 | } 39 | 40 | variable "runners_environment" { 41 | } 42 | 43 | variable "runners_group" { 44 | default = "default" 45 | } 46 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/rex/vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "git::https://github.com/jonico/terraform-aws-vpc.git?ref=2.1.1" 3 | 4 | environment = local.environment 5 | aws_region = local.aws_region 6 | create_private_hosted_zone = false 7 | } 8 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "terraform-up-and-running-state-jonico" 4 | key = "aws-github-runner-zelda/terraform.tfstate" 5 | region = "us-east-2" 6 | dynamodb_table = "terraform-up-and-running-locks-jonico" 7 | encrypt = true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/main.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | environment = var.runners_environment 4 | aws_region = "eu-west-1" 5 | } 6 | 7 | resource "random_password" "random" { 8 | length = 28 9 | } 10 | 11 | module "runners" { 12 | source = "philips-labs/github-runner/aws" 13 | version = "0.10.0" 14 | 15 | aws_region = local.aws_region 16 | vpc_id = module.vpc.vpc_id 17 | subnet_ids = module.vpc.private_subnets 18 | 19 | environment = local.environment 20 | tags = { 21 | Project = "octodemo-aws-github-runner-zelda" 22 | } 23 | 24 | github_app = { 25 | key_base64 = var.github_app_key_base64 26 | id = var.github_app_id 27 | client_id = var.github_app_client_id 28 | client_secret = var.github_app_client_secret 29 | webhook_secret = random_password.random.result 30 | } 31 | 32 | webhook_lambda_zip = "../download-lambdas/webhook.zip" 33 | runner_binaries_syncer_lambda_zip = "../download-lambdas/runner-binaries-syncer.zip" 34 | runners_lambda_zip = "../download-lambdas/runners.zip" 35 | 36 | enable_organization_runners = true 37 | runner_extra_labels = "auto-scale,aws,zelda" 38 | runner_group_name = var.runners_group 39 | 40 | # enable access to the runners via SSM 41 | enable_ssm_on_runners = true 42 | 43 | idle_config = [{ 44 | cron = "* * * * * *" 45 | timeZone = "Europe/Amsterdam" 46 | idleCount = var.runners_minimum_count 47 | }] 48 | 49 | # disable KMS and encryption 50 | # encrypt_secrets = false 51 | 52 | # Let the module manage the service linked role 53 | create_service_linked_role_spot = true 54 | 55 | # use GitHub Enterprise url 56 | ghes_url = var.github_enterprise_url 57 | 58 | instance_type = var.instance_type 59 | 60 | minimum_running_time_in_minutes = var.minimum_running_time_in_minutes 61 | 62 | runners_maximum_count = var.runners_maximum_count 63 | 64 | ami_owners = [var.runners_ami_owner] 65 | 66 | ami_filter = { 67 | name = [var.runners_ami_filter] 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/outputs.tf: -------------------------------------------------------------------------------- 1 | output "runners" { 2 | value = { 3 | lambda_syncer_name = module.runners.binaries_syncer.lambda.function_name 4 | } 5 | } 6 | 7 | output "webhook" { 8 | value = { 9 | secret = random_password.random.result 10 | endpoint = module.runners.webhook.endpoint 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.aws_region 3 | } 4 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/terraform.tfvars: -------------------------------------------------------------------------------- 1 | instance_type="t3.micro" 2 | minimum_running_time_in_minutes=10 3 | runners_maximum_count=3 4 | runners_minimum_count=0 5 | runners_environment="zelda" 6 | runners_ami_owner="869946923544" 7 | runners_ami_filter="actions-runner-with-additional-packages-*" 8 | runners_group="aws" 9 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/test-auto-scaling-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Scaling test for runner env zelda 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | numberJobs: 7 | description: 'Number of parallel jobs (should stay below 100)' 8 | required: true 9 | default: '3' 10 | 11 | jobs: 12 | generate-matrix: 13 | name: "Generate jobs for zelda" 14 | outputs: 15 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 16 | runs-on: [ self-hosted ] 17 | steps: 18 | - name: generate-matrix 19 | id: generate-matrix 20 | run: echo "::set-output name=matrix::[`seq -s , ${{ github.event.inputs.numberJobs }}`]" 21 | 22 | stress: 23 | name: "Stress tests for zelda" 24 | runs-on: [ aws, zelda ] 25 | needs: [generate-matrix] 26 | 27 | strategy: 28 | matrix: 29 | job-number: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 30 | 31 | steps: 32 | - name: Stress test ${{ matrix.job-number }} of env zelda 33 | run: echo "Matrix version ${{ matrix.job-number }}" && sleep $((RANDOM%120+60)) 34 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "github_app_key_base64" { 3 | sensitive = true 4 | } 5 | 6 | variable "github_app_id" { 7 | } 8 | 9 | variable "github_app_client_id" { 10 | } 11 | 12 | variable "github_app_client_secret" { 13 | sensitive = true 14 | } 15 | 16 | variable "github_enterprise_url" { 17 | default = "" 18 | } 19 | 20 | 21 | variable "instance_type" { 22 | default="t3.micro" 23 | } 24 | 25 | variable "minimum_running_time_in_minutes" { 26 | } 27 | 28 | variable "runners_maximum_count" { 29 | } 30 | 31 | variable "runners_minimum_count" { 32 | } 33 | 34 | variable "runners_ami_owner" { 35 | } 36 | 37 | variable "runners_ami_filter" { 38 | } 39 | 40 | variable "runners_environment" { 41 | } 42 | 43 | variable "runners_group" { 44 | default = "default" 45 | } 46 | -------------------------------------------------------------------------------- /actions-runner-envs-archived/zelda/vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "git::https://github.com/jonico/terraform-aws-vpc.git?ref=2.1.1" 3 | 4 | environment = local.environment 5 | aws_region = local.aws_region 6 | create_private_hosted_zone = false 7 | } 8 | -------------------------------------------------------------------------------- /actions-runner-envs/download-lambdas/main.tf: -------------------------------------------------------------------------------- 1 | module "lambdas" { 2 | source = "philips-labs/github-runner/aws//modules/download-lambda" 3 | lambdas = [ 4 | { 5 | name = "webhook" 6 | tag = "v0.12.0" 7 | }, 8 | { 9 | name = "runners" 10 | tag = "v0.12.0" 11 | }, 12 | { 13 | name = "runner-binaries-syncer" 14 | tag = "v0.12.0" 15 | } 16 | ] 17 | } 18 | 19 | output "files" { 20 | value = module.lambdas.files 21 | } 22 | -------------------------------------------------------------------------------- /actions-runner-envs/example/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "terraform-state-jonico" 4 | key = "aws-github-runner-example/terraform.tfstate" 5 | region = "us-east-2" 6 | dynamodb_table = "terraform-locks-jonico" 7 | encrypt = true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /actions-runner-envs/example/main.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | environment = var.runners_environment 4 | aws_region = "eu-west-1" 5 | } 6 | 7 | resource "random_password" "random" { 8 | length = 28 9 | } 10 | 11 | module "runners" { 12 | source = "philips-labs/github-runner/aws" 13 | version = "0.11.0" 14 | 15 | aws_region = local.aws_region 16 | vpc_id = module.vpc.vpc_id 17 | subnet_ids = module.vpc.private_subnets 18 | 19 | environment = local.environment 20 | tags = { 21 | Project = "octodemo-aws-github-runner-example" 22 | Stop = "Never" 23 | Terminate = "Never" 24 | } 25 | 26 | github_app = { 27 | key_base64 = var.github_app_key_base64 28 | id = var.github_app_id 29 | client_id = var.github_app_client_id 30 | client_secret = var.github_app_client_secret 31 | webhook_secret = random_password.random.result 32 | } 33 | 34 | webhook_lambda_zip = "../download-lambdas/webhook.zip" 35 | runner_binaries_syncer_lambda_zip = "../download-lambdas/runner-binaries-syncer.zip" 36 | runners_lambda_zip = "../download-lambdas/runners.zip" 37 | 38 | enable_organization_runners = true 39 | runner_extra_labels = "auto-scale,aws,example" 40 | runner_group_name = var.runners_group 41 | 42 | # enable access to the runners via SSM 43 | enable_ssm_on_runners = true 44 | 45 | idle_config = [{ 46 | cron = "* * * * * *" 47 | timeZone = "Europe/Amsterdam" 48 | idleCount = var.runners_minimum_count 49 | }] 50 | 51 | # disable KMS and encryption 52 | # encrypt_secrets = false 53 | 54 | # Let the module manage the service linked role 55 | create_service_linked_role_spot = true 56 | 57 | # use GitHub Enterprise url 58 | ghes_url = var.github_enterprise_url 59 | 60 | instance_type = var.instance_type 61 | 62 | minimum_running_time_in_minutes = var.minimum_running_time_in_minutes 63 | 64 | runners_maximum_count = var.runners_maximum_count 65 | 66 | ami_owners = [var.runners_ami_owner] 67 | 68 | ami_filter = { 69 | name = [var.runners_ami_filter] 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /actions-runner-envs/example/outputs.tf: -------------------------------------------------------------------------------- 1 | output "runners" { 2 | value = { 3 | lambda_syncer_name = module.runners.binaries_syncer.lambda.function_name 4 | } 5 | } 6 | 7 | output "webhook" { 8 | value = { 9 | secret = random_password.random.result 10 | endpoint = module.runners.webhook.endpoint 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /actions-runner-envs/example/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.aws_region 3 | } 4 | -------------------------------------------------------------------------------- /actions-runner-envs/example/terraform.tfvars: -------------------------------------------------------------------------------- 1 | instance_type="t3.micro" 2 | minimum_running_time_in_minutes=10 3 | runners_maximum_count=10 4 | runners_minimum_count=2 5 | runners_environment="example" 6 | runners_ami_owner="869946923544" 7 | runners_ami_filter="actions-runner-with-additional-packages-*" 8 | runners_group="aws" 9 | -------------------------------------------------------------------------------- /actions-runner-envs/example/test-auto-scaling-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Scaling test for runner env example 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | numberJobs: 7 | description: 'Number of parallel jobs (should stay below 100)' 8 | required: true 9 | default: '10' 10 | 11 | jobs: 12 | generate-matrix: 13 | name: "Generate jobs for example" 14 | outputs: 15 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 16 | runs-on: [ self-hosted ] 17 | steps: 18 | - name: generate-matrix 19 | id: generate-matrix 20 | run: echo "::set-output name=matrix::[`seq -s , ${{ github.event.inputs.numberJobs }}`]" 21 | 22 | stress: 23 | name: "Stress tests for example" 24 | runs-on: [ aws, example ] 25 | needs: [generate-matrix] 26 | 27 | strategy: 28 | matrix: 29 | job-number: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 30 | 31 | steps: 32 | - name: Stress test ${{ matrix.job-number }} of env example 33 | run: echo "Matrix version ${{ matrix.job-number }}" && sleep $((RANDOM%120+60)) 34 | -------------------------------------------------------------------------------- /actions-runner-envs/example/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "github_app_key_base64" { 3 | sensitive = true 4 | } 5 | 6 | variable "github_app_id" { 7 | } 8 | 9 | variable "github_app_client_id" { 10 | } 11 | 12 | variable "github_app_client_secret" { 13 | sensitive = true 14 | } 15 | 16 | variable "github_enterprise_url" { 17 | default = "" 18 | } 19 | 20 | 21 | variable "instance_type" { 22 | default="t3.micro" 23 | } 24 | 25 | variable "minimum_running_time_in_minutes" { 26 | } 27 | 28 | variable "runners_maximum_count" { 29 | } 30 | 31 | variable "runners_minimum_count" { 32 | } 33 | 34 | variable "runners_ami_owner" { 35 | } 36 | 37 | variable "runners_ami_filter" { 38 | } 39 | 40 | variable "runners_environment" { 41 | } 42 | 43 | variable "runners_group" { 44 | default = "default" 45 | } 46 | -------------------------------------------------------------------------------- /actions-runner-envs/example/vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "git::https://github.com/philips-software/terraform-aws-vpc.git?ref=2.2.0" 3 | 4 | environment = local.environment 5 | aws_region = local.aws_region 6 | create_private_hosted_zone = false 7 | } 8 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/backend.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | backend "s3" { 3 | bucket = "terraform-state-jonico" 4 | key = "aws-github-runner-${RUNNER_ENVIRONMENT}/terraform.tfstate" 5 | region = "us-east-2" 6 | dynamodb_table = "terraform-locks-jonico" 7 | encrypt = true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/main.tf: -------------------------------------------------------------------------------- 1 | 2 | locals { 3 | environment = var.runners_environment 4 | aws_region = "eu-west-1" 5 | } 6 | 7 | resource "random_password" "random" { 8 | length = 28 9 | } 10 | 11 | module "runners" { 12 | source = "philips-labs/github-runner/aws" 13 | version = "0.12.0" 14 | 15 | aws_region = local.aws_region 16 | vpc_id = module.vpc.vpc_id 17 | subnet_ids = module.vpc.private_subnets 18 | 19 | environment = local.environment 20 | tags = { 21 | Project = "octodemo-aws-github-runner-${RUNNER_ENVIRONMENT}" 22 | Stop = "Never" 23 | Terminate = "Never" 24 | } 25 | 26 | github_app = { 27 | key_base64 = var.github_app_key_base64 28 | id = var.github_app_id 29 | client_id = var.github_app_client_id 30 | client_secret = var.github_app_client_secret 31 | webhook_secret = random_password.random.result 32 | } 33 | 34 | webhook_lambda_zip = "../download-lambdas/webhook.zip" 35 | runner_binaries_syncer_lambda_zip = "../download-lambdas/runner-binaries-syncer.zip" 36 | runners_lambda_zip = "../download-lambdas/runners.zip" 37 | 38 | enable_organization_runners = true 39 | runner_extra_labels = "auto-scale,aws,${RUNNER_ENVIRONMENT}" 40 | runner_group_name = var.runners_group 41 | 42 | # enable access to the runners via SSM 43 | enable_ssm_on_runners = true 44 | 45 | idle_config = [{ 46 | cron = "* * * * * *" 47 | timeZone = "Europe/Amsterdam" 48 | idleCount = var.runners_minimum_count 49 | }] 50 | 51 | # disable KMS and encryption 52 | # encrypt_secrets = false 53 | 54 | # Let the module manage the service linked role 55 | create_service_linked_role_spot = true 56 | 57 | # use GitHub Enterprise url if set 58 | ghes_url = var.github_enterprise_url != "" ? var.github_enterprise_url : null 59 | 60 | instance_type = var.instance_type 61 | 62 | minimum_running_time_in_minutes = var.minimum_running_time_in_minutes 63 | 64 | runners_maximum_count = var.runners_maximum_count 65 | 66 | ami_owners = [var.runners_ami_owner] 67 | 68 | ami_filter = { 69 | name = [var.runners_ami_filter] 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/outputs.tf: -------------------------------------------------------------------------------- 1 | output "runners" { 2 | value = { 3 | lambda_syncer_name = module.runners.binaries_syncer.lambda.function_name 4 | } 5 | } 6 | 7 | output "webhook" { 8 | value = { 9 | secret = random_password.random.result 10 | endpoint = module.runners.webhook.endpoint 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/providers.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | region = local.aws_region 3 | } 4 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/terraform.tfvars: -------------------------------------------------------------------------------- 1 | instance_type="t3.micro" 2 | minimum_running_time_in_minutes=10 3 | runners_maximum_count=${RUNNER_MAX_REPLICAS} 4 | runners_minimum_count=${RUNNER_MIN_REPLICAS} 5 | runners_environment="${RUNNER_ENVIRONMENT}" 6 | runners_ami_owner="${RUNNER_AMI_OWNER}" 7 | runners_ami_filter="${RUNNER_AMI_FILTER}" 8 | runners_group="${RUNNER_GROUP}" 9 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/test-auto-scaling-workflow.yml: -------------------------------------------------------------------------------- 1 | name: Scaling test for runner env ${RUNNER_ENVIRONMENT} 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | numberJobs: 7 | description: 'Number of parallel jobs (should stay below 100)' 8 | required: true 9 | default: '${RUNNER_MAX_REPLICAS}' 10 | 11 | jobs: 12 | generate-matrix: 13 | name: "Generate jobs for ${RUNNER_ENVIRONMENT}" 14 | outputs: 15 | matrix: ${{ steps.generate-matrix.outputs.matrix }} 16 | runs-on: [ Linux ] 17 | steps: 18 | - name: generate-matrix 19 | id: generate-matrix 20 | run: echo "::set-output name=matrix::[`seq -s , ${{ github.event.inputs.numberJobs }}`]" 21 | 22 | stress: 23 | name: "Stress tests for ${RUNNER_ENVIRONMENT}" 24 | runs-on: [ aws, ${RUNNER_ENVIRONMENT} ] 25 | needs: [generate-matrix] 26 | 27 | strategy: 28 | matrix: 29 | job-number: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} 30 | 31 | steps: 32 | - name: Stress test ${{ matrix.job-number }} of env ${RUNNER_ENVIRONMENT} 33 | run: echo "Matrix version ${{ matrix.job-number }}" && sleep $((RANDOM%120+60)) 34 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/variables.tf: -------------------------------------------------------------------------------- 1 | 2 | variable "github_app_key_base64" { 3 | sensitive = true 4 | } 5 | 6 | variable "github_app_id" { 7 | } 8 | 9 | variable "github_app_client_id" { 10 | } 11 | 12 | variable "github_app_client_secret" { 13 | sensitive = true 14 | } 15 | 16 | variable "github_enterprise_url" { 17 | default = "" 18 | } 19 | 20 | 21 | variable "instance_type" { 22 | default="t3.micro" 23 | } 24 | 25 | variable "minimum_running_time_in_minutes" { 26 | } 27 | 28 | variable "runners_maximum_count" { 29 | } 30 | 31 | variable "runners_minimum_count" { 32 | } 33 | 34 | variable "runners_ami_owner" { 35 | } 36 | 37 | variable "runners_ami_filter" { 38 | } 39 | 40 | variable "runners_environment" { 41 | } 42 | 43 | variable "runners_group" { 44 | default = "default" 45 | } 46 | -------------------------------------------------------------------------------- /actions-runner-envs/template-env/vpc.tf: -------------------------------------------------------------------------------- 1 | module "vpc" { 2 | source = "git::https://github.com/philips-software/terraform-aws-vpc.git?ref=2.2.0" 3 | 4 | environment = local.environment 5 | aws_region = local.aws_region 6 | create_private_hosted_zone = false 7 | } 8 | -------------------------------------------------------------------------------- /bootstrap-terraform-state-bucket/main.tf: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = ">= 0.12, < 0.15" 3 | # Uncomment once you ran terraform apply and have created your bucket and DB 4 | #backend "s3" { 5 | # bucket = "terraform-state-jonico" 6 | # key = "global/s3/terraform.tfstate" 7 | # region = "us-east-2" 8 | # dynamodb_table = "terraform-locks-jonico" 9 | # encrypt = true 10 | #} 11 | } 12 | 13 | provider "aws" { 14 | region = "us-east-2" 15 | 16 | } 17 | 18 | resource "aws_s3_bucket" "terraform_state" { 19 | 20 | bucket = var.bucket_name 21 | 22 | lifecycle { 23 | prevent_destroy= true 24 | } 25 | 26 | # Enable versioning so we can see the full revision history of our 27 | # state files 28 | versioning { 29 | enabled = true 30 | } 31 | 32 | # Enable server-side encryption by default 33 | server_side_encryption_configuration { 34 | rule { 35 | apply_server_side_encryption_by_default { 36 | sse_algorithm = "AES256" 37 | } 38 | } 39 | } 40 | } 41 | 42 | resource "aws_dynamodb_table" "terraform_locks" { 43 | name = var.table_name 44 | billing_mode = "PAY_PER_REQUEST" 45 | hash_key = "LockID" 46 | 47 | attribute { 48 | name = "LockID" 49 | type = "S" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bootstrap-terraform-state-bucket/outputs.tf: -------------------------------------------------------------------------------- 1 | output "s3_bucket_arn" { 2 | value = aws_s3_bucket.terraform_state.arn 3 | description = "The ARN of the S3 bucket" 4 | } 5 | 6 | output "dynamodb_table_name" { 7 | value = aws_dynamodb_table.terraform_locks.name 8 | description = "The name of the DynamoDB table" 9 | } 10 | -------------------------------------------------------------------------------- /bootstrap-terraform-state-bucket/variables.tf: -------------------------------------------------------------------------------- 1 | variable "bucket_name" { 2 | description = "The name of the S3 bucket. Must be globally unique." 3 | type = string 4 | default = "terraform-state-jonico" 5 | } 6 | 7 | variable "table_name" { 8 | description = "The name of the DynamoDB table. Must be unique in this AWS account." 9 | type = string 10 | default = "terraform-locks-jonico" 11 | } 12 | -------------------------------------------------------------------------------- /custom-images/actions-runner-with-additional-packages.json: -------------------------------------------------------------------------------- 1 | { 2 | "variables": { 3 | "aws_access_key": "", 4 | "aws_secret_key": "" 5 | }, 6 | "builders": [ 7 | { 8 | "type": "amazon-ebs", 9 | "access_key": "{{user `aws_access_key`}}", 10 | "secret_key": "{{user `aws_secret_key`}}", 11 | "region": "eu-west-1", 12 | "source_ami_filter": { 13 | "filters": { 14 | "virtualization-type": "hvm", 15 | "name": "amzn2-ami-hvm-2.*-x86_64-ebs", 16 | "root-device-type": "ebs" 17 | }, 18 | "owners": "amazon", 19 | "most_recent": true 20 | }, 21 | "instance_type": "t3.micro", 22 | "ssh_username": "ec2-user", 23 | "ami_name": "actions-runner-with-additional-packages-{{timestamp}}", 24 | "ami_regions": ["eu-west-1", "eu-west-2", "us-east-1", "us-east-2"] 25 | } 26 | ], 27 | "provisioners": [ 28 | { 29 | "type": "shell", 30 | "inline": ["sudo amazon-linux-extras install epel -y", "sudo yum install stress -y" ] 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /docs/architecture.drawio: -------------------------------------------------------------------------------- 1 | 7Vxbc5s4FP41ntl9SAYQNz/6kmS7TbdJ026m++KRjYxpMHJBjp38+hUgMLrYITa2k9ZpZooOQhI63/nOOZJIC/Smy6sYziafsIfClqF5yxbotwxDb+tt+l8qecolrmnnAj8OPFZpJbgLnhETakw6DzyUcBUJxiEJZrxwhKMIjQgng3GMF3y1MQ75XmfQR5LgbgRDWXofeGTC3sJwVvK/UOBPip51m73wFBaV2ZskE+jhRUUELlqgF2NM8qvpsofCdPKKecmfu1xztxxYjCJS5wEtxN/CpX3739fn0Rfju/sd3v44Sx9Im3mE4Zy9cef+jgp6IZ57bODkqZiNGQ4iks2o1aW/tMOe1rLonV5aOjcsQSCWHV6gy6W0DV4glh1eoIvN60L/ujjAikAqcc1rQv9aZYD0F3TxnIRBhHol9jQq9GPoBVQnPRzimMoiHNHZ607INKQlnV4uJgFBdzM4Smd1Qe2GysY4Igz9ulGU2cSnrVL0zNLr6dJPDe0cLhLz3I/xfJZ1+YHiX3l3QC8Ho0yZtBES4wdUDKxlAPrvMkVLdxyEoTDgRxSTgBpCJwz8tG2C064gK4VoTNIW6VsEkX+dlfpAYyNXdeHBZII89joyeBme017RsiJiYL5CeIpI/ESrFHdtZliMWUxWXKzM1DatXDapmKhbUAtk1OCXTa+sh14wA1Ib0+eu24eTeDyIoD74eNN5SD78e9aWbKmPF1GIITUkbRhEkPVRtSjkUbphRRyTCfZxBMOLlbRLtRh55cSt6lzjVCEZoH4gQp4YeuCcYB5uIRyisAtHD37WlKDmtZpI8DweoU3cARgfw9hHZFNFNjHpu27UbIxCSIJHnnpVamKP3qR0tEKEbVgcIuy2zTeRj5Q9tVJ2J47hU6UaY7n1/bTbfD9AYN7X1acX+QhWyCvnpBYYN2qngsYvc8pTcSJhUGH8ElNYHbvn2lUz1tdyhMhdAiOUTW0JzPoUYek8RQAFRxhMGRxHGI6AnG04QqkWW9LKzee7r6lxPCKGymPQww1OAhJkPmREx4HiinKvhQq8I5CqF1gYYkLwdI/8U4SDL9GP2TT77AQAXQJA5+YDFVxBghZQdhDKGGOdjxVjD3qvbVr9S6Nyrx/EtKFclVGKF8H9ZxYKtK6looFx9iN683VIKSGwOZYoIVRB5UvxEExm+XSMgyXy1gVIMcpRlIdHXVpUBUpwFgx8Nv0HYCWgAY6VdENmJceVSamQNQ5JOQe4R8MJxg9b0FFFhSjyOmkGlgIhxKOHVBQOs3KBhwxiMCZFPTbJ9MnLICzakX2RaXVtU+K+tQS0PcXUjXD0mhRT0bCuMa3vGPQYDu/jygS0aCJ/SSnokRuyxYbcc6tW/NRUyCJHLFcBmcyHVNaZzVqGHaYpxzCmVz4p9VqBJ0dKSuKswLNgnzi3MMkPsgxnHbdNA88LVXlSeUNkpAWiZuDj5Nxnb7Uzk9gikQCZSBQZkGOtB9lOCZDhyjEn+jlHSQqzeB69pTDnBfXyHqrAyMFjHLMmAVlNxzhbJUYW4BMdg3HRXhMds7b32jWUunTcC818XSjV16ye7vw2oVQIp0MPHiS30wR3dcAoSk1+8vLPMXhue66x6gY72psgG9MQkntt8yqMWN/UDrAKY60jp9s5ohJDu7tNV9vl2OaPPp28J+R9QkkCffRn82QGTNN9ZV7Y7enAsn8bMkt+JsfIB8sVqWMxma4K4/wgSZWRxXFRenH8EO79rFRpMrX+82xf28Rd2IOHf8jfD/cfR+asdh65Y86omwIVik00tFAuppSmDjZS9Av1eYpePV0MB4/HCdpLZloosGIR+R6xoZWL6tp8doo4f+WIEwgRJ3CNg/G0et1O5umvKJ4GESRI3uU5EfQG+667Q8mgcKada8Bl078jGVuaw8HKcpxaZLwn/lNHBIrNRDTFj+gUD+wCt8bigRUsLUc3doPlAbypnCnL3jQ9OnHyp0f0p3vwmBZwj+wxZeR1MvUnHPa66VmdQOFDd8WfrXUAcF6HP8NxdP03SrrBIWI5W8g07PaRc+4i538vi4Wg9tZo44e/dptn/RedZ9D4FtBOTAvkiLFZNmVcUG+FklV+czyZLaCg+CLdFE1YI0ruzLZNBySYosEQJtmze/DPh2RBNWqcd2addfdMmj8Cu9sKr3wK7dssP6ismP7MQlrKwxNrc6jyFAR188EzHJY2wH9C0N/C2W9EjgT+8tMQNoZW9euLNUkUsIHNGcaOKVVpblyjZ0Ke30jCtSmNVB2s+Rz7MKIKyshPVP77Ok9ziLCR36kBxUmaAxy42cg/nB17kKCu+muDU/78C61Hm9Yby2GMLUO+fatfoU1wnswwGQRRQmA0UgSSl5ZrAVNhBQ2eYLFdTn+m4usEV/FxQiFrXH/ySapejE57CQ1s9m78Qqfu6i6NS9rCAbty2WDXyMQRoCg2UfcQsaPxA3RNAawN7SybjnoHe+3hn8311+0sL4VpFqd9/zsvsoM/ceqmAE3IaE1bjtAOyqny914n/W0MsAX9KWKahvRHi6uP73NzXf0JA3DxPw== -------------------------------------------------------------------------------- /docs/architecture.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
AWS Cloud
AWS Cloud
Download binary
Download binary
Runners
POST event
POST event
API Gateway
API Gateway
Webhook
Webhook
Github App
Github App
Request run event
Request run event
Webhook
Webhook
WebhookQueue SQS
(DelayedMessage)
WebhookQueue...
Register runner
Register runner
Scale Runners up
Scale Runners...
Terminates
Terminates
Remove runner
Remove runner
Scale Runners Down
Scale Runners...
Actions Runners Binaries
Actions Runne...
Upload
Upload
Github Organization
Github Organi...
UpdateBinary
UpdateBinary
Creates
Creates
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /docs/component-overview.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
AWS Cloud
AWS Cloud
Download binary
Download binary
Runners
POST event
POST event
API Gateway
API Gateway
Webhook
Webhook
Github App
Github App
Request run event
Request run event
Webhook
Webhook
WebhookQueue SQS
(DelayedMessage)
WebhookQueue...
Register runner
Register runner
Scale Runners up
Scale Runners...
Terminates
Terminates
Remove runner
Remove runner
Scale Runners Down
Scale Runners...
Actions Runners Binaries
Actions Runne...
Upload
Upload
Github Organization
Github Organi...
UpdateBinary
UpdateBinary
Creates
Creates
Viewer does not support full SVG 1.1
--------------------------------------------------------------------------------