Skip to content

Add CI pipeline for creating self-hosted runners #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Dec 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 145 additions & 0 deletions .github/workflows/create-azure-self-hosted-runners.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
name: create-azure-self-hosted-runners

on:
workflow_dispatch:
inputs:
amount_of_runners:
description: 'Amount of runners to set up'
required: true
default: 1
runner_scope:
type: choice
required: true
description: Scope of the runner. On personal accounts, only "repo-level" works
options:
- org-level
- repo-level
default: repo-level
runner_org:
type: string
required: false
description: Organization or personal account to deploy the runner to (defaults to the repository owner)
runner_repo:
type: string
required: false
description: Repo to deploy the runner to. Only needed if runner_scope is set to "repo-level" (defaults to current repository)

env:
AMOUNT_OF_RUNNERS: ${{ github.event.inputs.amount_of_runners }}
ACTIONS_RUNNER_SCOPE: ${{ github.event.inputs.runner_scope }}
ACTIONS_RUNNER_ORG: "${{ github.event.inputs.runner_org || github.repository_owner }}"
ACTIONS_RUNNER_REPO: "${{ github.event.inputs.runner_repo || github.event.repository.name }}"
# This has to be a public URL that the VM can access after creation
POST_DEPLOYMENT_SCRIPT_URL: https://raw.githubusercontent.com/${{ github.repository }}/${{ github.ref_name }}/azure-self-hosted-runners/post-deployment-script.ps1

# The following secrets are required for this workflow to run:
# AZURE_CREDENTIALS - Credentials for the Azure CLI. It's recommended to set up a resource
# group specifically for self-hosted Actions Runners.
# az ad sp create-for-rbac --name "{YOUR_DESCRIPTIVE_NAME_HERE}" --role contributor \
# --scopes /subscriptions/{SUBSCRIPTION_ID_HERE}/resourceGroups/{RESOURCE_GROUP_HERE} \
# --sdk-auth
# AZURE_RESOURCE_GROUP - Resource group to create the runner(s) in
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed that you want to create these VMs in their own, dedicated resource group. This pipeline expects the resource group to be created in Azure prior to running the pipeline.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense. Then we can delete the resource group which should delete everything contained therein.

# AZURE_VM_USERNAME - Username of the VM so you can RDP into it
# AZURE_VM_PASSWORD - Password of the VM so you can RDP into it
jobs:
create-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.create-matrix.outputs.matrix }}
steps:
- name: Create matrix for setting up runners in parallel
id: create-matrix
run: |
echo "Going to create $AMOUNT_OF_RUNNERS runners"
MATRIX="matrix={\"runner_index\":[$(seq -s "," 1 $AMOUNT_OF_RUNNERS)]}"
echo "Going to use this matrix: $MATRIX"
echo $MATRIX >> $GITHUB_OUTPUT
create-runners:
name: create-runner-${{ matrix.runner_index }}
needs: create-matrix
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.create-matrix.outputs.matrix) }}
outputs:
vm_name: ${{ steps.generate-vm-name.outputs.vm_name }}
steps:
- name: Generate VM name
id: generate-vm-name
run: |
VM_NAME="actions-runner-$(date +%Y%m%d%H%M%S%N)"
echo "Will be using $VM_NAME as the VM name"
echo "vm_name=$VM_NAME" >> $GITHUB_OUTPUT
- uses: actions/checkout@v3
- name: Obtain installation token
id: setup
uses: actions/github-script@v6
with:
script: |
const appId = ${{ secrets.GH_APP_ID }}
const privateKey = `${{ secrets.GH_APP_PRIVATE_KEY }}`

const getAppInstallationId = require('./get-app-installation-id')
const installationId = await getAppInstallationId(
console,
appId,
privateKey,
process.env.ACTIONS_RUNNER_ORG,
process.env.ACTIONS_RUNNER_REPO
)

const getInstallationAccessToken = require('./get-installation-access-token')
const accessToken = await getInstallationAccessToken(
console,
appId,
privateKey,
installationId
)

core.setSecret(accessToken)
core.setOutput('token', accessToken)
# We can't use the octokit/request-action as we can't properly mask the runner token with it
# https://github.com/actions/runner/issues/475
- name: Generate Actions Runner token and registration URL
run: |
case "$ACTIONS_RUNNER_SCOPE" in
"org-level")
ACTIONS_API_URL="https://api.github.com/repos/${{ env.ACTIONS_RUNNER_ORG }}/actions/runners/registration-token"
echo ACTIONS_RUNNER_REGISTRATION_URL="https://github.com/${{ env.ACTIONS_RUNNER_ORG }}" >> $GITHUB_ENV
;;
"repo-level")
ACTIONS_API_URL="https://api.github.com/repos/${{ env.ACTIONS_RUNNER_ORG }}/${{ env.ACTIONS_RUNNER_REPO }}/actions/runners/registration-token"
echo ACTIONS_RUNNER_REGISTRATION_URL="https://github.com/${{ env.ACTIONS_RUNNER_ORG }}/${{ env.ACTIONS_RUNNER_REPO }}" >> $GITHUB_ENV
;;
*)
echo "Unsupported runner scope: $ACTIONS_RUNNER_SCOPE"
exit 1
;;
esac

ACTIONS_RUNNER_TOKEN=$(curl \
-X POST \
-H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ steps.setup.outputs.token }}"\
-H "X-GitHub-Api-Version: 2022-11-28" \
$ACTIONS_API_URL \
| jq --raw-output .token)
echo "::add-mask::$ACTIONS_RUNNER_TOKEN"
echo ACTIONS_RUNNER_TOKEN=$ACTIONS_RUNNER_TOKEN >> $GITHUB_ENV

- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}

- uses: azure/arm-deploy@v1
with:
resourceGroupName: ${{ secrets.AZURE_RESOURCE_GROUP }}
template: ./azure-self-hosted-runners/azure-arm-template.json
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 }}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Standard_D8pls_v5 is an 8-core arm64 VM with 16GB of RAM. A machine with less specs might be sufficient too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's leave it for now, I'll monitor the usage. If I'm running out of money too fast, I'll just scale it down.


- name: Deallocate the VM for later use
uses: azure/CLI@v1
with:
azcliversion: 2.43.0
inlineScript: |
az vm deallocate -n ${{ steps.generate-vm-name.outputs.vm_name }} -g ${{ secrets.AZURE_RESOURCE_GROUP }} --verbose
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"location": {
"value": "westeurope"
},
"enableAcceleratedNetworking": {
"value": true
},
"networkSecurityGroupRules": {
"value": [
{
"name": "RDP",
"properties": {
"priority": 300,
"protocol": "TCP",
"access": "Allow",
"direction": "Inbound",
"sourceAddressPrefix": "*",
"sourcePortRange": "*",
"destinationAddressPrefix": "*",
"destinationPortRange": "3389"
}
}
]
},
"subnetName": {
"value": "default"
},
"addressPrefixes": {
"value": [
"10.2.0.0/16"
]
},
"subnets": {
"value": [
{
"name": "default",
"properties": {
"addressPrefix": "10.2.0.0/24"
}
}
]
},
"publicIpAddressType": {
"value": "Static"
},
"publicIpAddressSku": {
"value": "Standard"
},
"pipDeleteOption": {
"value": "Detach"
},
"osDiskType": {
"value": "Premium_LRS"
},
"osDiskDeleteOption": {
"value": "Delete"
},
"nicDeleteOption": {
"value": "Detach"
},
"patchMode": {
"value": "AutomaticByOS"
},
"enableHotpatching": {
"value": false
},
"zone": {
"value": "1"
},
"computerName": {
"value": "actions-runner"
}
}
}
Loading