Skip to content

Commit a538b16

Browse files
authored
Merge pull request #5 from dennisameling/self-hosted-runners
Add CI pipeline for creating self-hosted runners
2 parents 254feea + b09684a commit a538b16

File tree

4 files changed

+611
-0
lines changed

4 files changed

+611
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
name: create-azure-self-hosted-runners
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
amount_of_runners:
7+
description: 'Amount of runners to set up'
8+
required: true
9+
default: 1
10+
runner_scope:
11+
type: choice
12+
required: true
13+
description: Scope of the runner. On personal accounts, only "repo-level" works
14+
options:
15+
- org-level
16+
- repo-level
17+
default: repo-level
18+
runner_org:
19+
type: string
20+
required: false
21+
description: Organization or personal account to deploy the runner to (defaults to the repository owner)
22+
runner_repo:
23+
type: string
24+
required: false
25+
description: Repo to deploy the runner to. Only needed if runner_scope is set to "repo-level" (defaults to current repository)
26+
27+
env:
28+
AMOUNT_OF_RUNNERS: ${{ github.event.inputs.amount_of_runners }}
29+
ACTIONS_RUNNER_SCOPE: ${{ github.event.inputs.runner_scope }}
30+
ACTIONS_RUNNER_ORG: "${{ github.event.inputs.runner_org || github.repository_owner }}"
31+
ACTIONS_RUNNER_REPO: "${{ github.event.inputs.runner_repo || github.event.repository.name }}"
32+
# This has to be a public URL that the VM can access after creation
33+
POST_DEPLOYMENT_SCRIPT_URL: https://raw.githubusercontent.com/${{ github.repository }}/${{ github.ref_name }}/azure-self-hosted-runners/post-deployment-script.ps1
34+
35+
# The following secrets are required for this workflow to run:
36+
# AZURE_CREDENTIALS - Credentials for the Azure CLI. It's recommended to set up a resource
37+
# group specifically for self-hosted Actions Runners.
38+
# az ad sp create-for-rbac --name "{YOUR_DESCRIPTIVE_NAME_HERE}" --role contributor \
39+
# --scopes /subscriptions/{SUBSCRIPTION_ID_HERE}/resourceGroups/{RESOURCE_GROUP_HERE} \
40+
# --sdk-auth
41+
# AZURE_RESOURCE_GROUP - Resource group to create the runner(s) in
42+
# AZURE_VM_USERNAME - Username of the VM so you can RDP into it
43+
# AZURE_VM_PASSWORD - Password of the VM so you can RDP into it
44+
jobs:
45+
create-matrix:
46+
runs-on: ubuntu-latest
47+
outputs:
48+
matrix: ${{ steps.create-matrix.outputs.matrix }}
49+
steps:
50+
- name: Create matrix for setting up runners in parallel
51+
id: create-matrix
52+
run: |
53+
echo "Going to create $AMOUNT_OF_RUNNERS runners"
54+
MATRIX="matrix={\"runner_index\":[$(seq -s "," 1 $AMOUNT_OF_RUNNERS)]}"
55+
echo "Going to use this matrix: $MATRIX"
56+
echo $MATRIX >> $GITHUB_OUTPUT
57+
create-runners:
58+
name: create-runner-${{ matrix.runner_index }}
59+
needs: create-matrix
60+
runs-on: ubuntu-latest
61+
strategy:
62+
matrix: ${{ fromJSON(needs.create-matrix.outputs.matrix) }}
63+
outputs:
64+
vm_name: ${{ steps.generate-vm-name.outputs.vm_name }}
65+
steps:
66+
- name: Generate VM name
67+
id: generate-vm-name
68+
run: |
69+
VM_NAME="actions-runner-$(date +%Y%m%d%H%M%S%N)"
70+
echo "Will be using $VM_NAME as the VM name"
71+
echo "vm_name=$VM_NAME" >> $GITHUB_OUTPUT
72+
- uses: actions/checkout@v3
73+
- name: Obtain installation token
74+
id: setup
75+
uses: actions/github-script@v6
76+
with:
77+
script: |
78+
const appId = ${{ secrets.GH_APP_ID }}
79+
const privateKey = `${{ secrets.GH_APP_PRIVATE_KEY }}`
80+
81+
const getAppInstallationId = require('./get-app-installation-id')
82+
const installationId = await getAppInstallationId(
83+
console,
84+
appId,
85+
privateKey,
86+
process.env.ACTIONS_RUNNER_ORG,
87+
process.env.ACTIONS_RUNNER_REPO
88+
)
89+
90+
const getInstallationAccessToken = require('./get-installation-access-token')
91+
const accessToken = await getInstallationAccessToken(
92+
console,
93+
appId,
94+
privateKey,
95+
installationId
96+
)
97+
98+
core.setSecret(accessToken)
99+
core.setOutput('token', accessToken)
100+
# We can't use the octokit/request-action as we can't properly mask the runner token with it
101+
# https://github.com/actions/runner/issues/475
102+
- name: Generate Actions Runner token and registration URL
103+
run: |
104+
case "$ACTIONS_RUNNER_SCOPE" in
105+
"org-level")
106+
ACTIONS_API_URL="https://api.github.com/repos/${{ env.ACTIONS_RUNNER_ORG }}/actions/runners/registration-token"
107+
echo ACTIONS_RUNNER_REGISTRATION_URL="https://github.com/${{ env.ACTIONS_RUNNER_ORG }}" >> $GITHUB_ENV
108+
;;
109+
"repo-level")
110+
ACTIONS_API_URL="https://api.github.com/repos/${{ env.ACTIONS_RUNNER_ORG }}/${{ env.ACTIONS_RUNNER_REPO }}/actions/runners/registration-token"
111+
echo ACTIONS_RUNNER_REGISTRATION_URL="https://github.com/${{ env.ACTIONS_RUNNER_ORG }}/${{ env.ACTIONS_RUNNER_REPO }}" >> $GITHUB_ENV
112+
;;
113+
*)
114+
echo "Unsupported runner scope: $ACTIONS_RUNNER_SCOPE"
115+
exit 1
116+
;;
117+
esac
118+
119+
ACTIONS_RUNNER_TOKEN=$(curl \
120+
-X POST \
121+
-H "Accept: application/vnd.github+json" \
122+
-H "Authorization: Bearer ${{ steps.setup.outputs.token }}"\
123+
-H "X-GitHub-Api-Version: 2022-11-28" \
124+
$ACTIONS_API_URL \
125+
| jq --raw-output .token)
126+
echo "::add-mask::$ACTIONS_RUNNER_TOKEN"
127+
echo ACTIONS_RUNNER_TOKEN=$ACTIONS_RUNNER_TOKEN >> $GITHUB_ENV
128+
129+
- name: Azure Login
130+
uses: azure/login@v1
131+
with:
132+
creds: ${{ secrets.AZURE_CREDENTIALS }}
133+
134+
- uses: azure/arm-deploy@v1
135+
with:
136+
resourceGroupName: ${{ secrets.AZURE_RESOURCE_GROUP }}
137+
template: ./azure-self-hosted-runners/azure-arm-template.json
138+
parameters: ./azure-self-hosted-runners/azure-arm-template-example-parameters.json githubActionsRunnerRegistrationUrl="${{ env.ACTIONS_RUNNER_REGISTRATION_URL }}" githubActionsRunnerToken="${{ env.ACTIONS_RUNNER_TOKEN }}" postDeploymentPsScriptUrl="${{ env.POST_DEPLOYMENT_SCRIPT_URL }}" virtualMachineName=${{ steps.generate-vm-name.outputs.vm_name }} virtualMachineSize=Standard_D8pls_v5 publicIpAddressName1=${{ steps.generate-vm-name.outputs.vm_name }}-ip adminUsername=${{ secrets.AZURE_VM_USERNAME }} adminPassword=${{ secrets.AZURE_VM_PASSWORD }}
139+
140+
- name: Deallocate the VM for later use
141+
uses: azure/CLI@v1
142+
with:
143+
azcliversion: 2.43.0
144+
inlineScript: |
145+
az vm deallocate -n ${{ steps.generate-vm-name.outputs.vm_name }} -g ${{ secrets.AZURE_RESOURCE_GROUP }} --verbose
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"location": {
6+
"value": "westeurope"
7+
},
8+
"enableAcceleratedNetworking": {
9+
"value": true
10+
},
11+
"networkSecurityGroupRules": {
12+
"value": [
13+
{
14+
"name": "RDP",
15+
"properties": {
16+
"priority": 300,
17+
"protocol": "TCP",
18+
"access": "Allow",
19+
"direction": "Inbound",
20+
"sourceAddressPrefix": "*",
21+
"sourcePortRange": "*",
22+
"destinationAddressPrefix": "*",
23+
"destinationPortRange": "3389"
24+
}
25+
}
26+
]
27+
},
28+
"subnetName": {
29+
"value": "default"
30+
},
31+
"addressPrefixes": {
32+
"value": [
33+
"10.2.0.0/16"
34+
]
35+
},
36+
"subnets": {
37+
"value": [
38+
{
39+
"name": "default",
40+
"properties": {
41+
"addressPrefix": "10.2.0.0/24"
42+
}
43+
}
44+
]
45+
},
46+
"publicIpAddressType": {
47+
"value": "Static"
48+
},
49+
"publicIpAddressSku": {
50+
"value": "Standard"
51+
},
52+
"pipDeleteOption": {
53+
"value": "Detach"
54+
},
55+
"osDiskType": {
56+
"value": "Premium_LRS"
57+
},
58+
"osDiskDeleteOption": {
59+
"value": "Delete"
60+
},
61+
"nicDeleteOption": {
62+
"value": "Detach"
63+
},
64+
"patchMode": {
65+
"value": "AutomaticByOS"
66+
},
67+
"enableHotpatching": {
68+
"value": false
69+
},
70+
"zone": {
71+
"value": "1"
72+
},
73+
"computerName": {
74+
"value": "actions-runner"
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)