
Cloud Consultant | AWS & Azure | DevOps |Infrastructure Automation with Ansible & Jenkins | Docker & Kubernetes Enthusiast| Passionate about Scalable & Secure Cloud Solutions

Introduction
Dynamically register a GitHub Actions self-hosted runner using Terraform and a shell script. This approach ensures that sensitive tokens are never hardcoded and that runners are registered securely and automatically using the GitHub API.
Learning Objectives
- Why dynamic runner registration is important
- How to securely generate and use a GitHub API token
- Project structure overview
- Terraform configuration to trigger a shell script
- Secure and automated runner registration using GitHub API
Why Dynamic Runner Token Fetching?
GitHub requires a registration token to register a self-hosted runner, which is valid for only 60 minutes. Hardcoding this token poses a security risk and is inefficient. Instead, we fetch it dynamically during deployment using GitHub’s REST API, ensuring security and flexibility.
Visual workflow


Step 1: Generate a GitHub Personal Access Token (PAT)
- Go to GitHub > Settings > Developer settings > Personal access tokens
- Click “Generate new token” (Classic or Fine-grained)
- Required scopes:
- repo
- admin:repo_hook
- repo
This token will be used to authenticate API calls to GitHub. Keep it secret!
Step 2: Project Structure
- main.tf
- variables.tf
- get-runner-token.sh
Step 3: Define Variables (variables.tf)
variable "github_repo" {
description = "GitHub repository (e.g., your-org/nodejs-app)"
type = string
}
variable "github_pat" {
description = "GitHub Personal Access Token"
type = string
sensitive = true
}
Step 4: Bash Script to Fetch Token and Use Token (get-runner-token.sh)
#!/bin/bash
REPO_OWNER="your-org"
REPO_NAME="your-repo"
GITHUB_PAT=$1 # Pass token securely as parameter
LABELS="ec2-runner"
RUNNER_NAME="ec2-runner-$(hostname)"
sudo apt update && sudo apt install -y jq curl
REG_TOKEN=$(curl -s -X POST \
-H "Authorization: token $GITHUB_PAT" \
https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/runners/registration-token \
| jq .token --raw-output)
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64.tar.gz -L https://github.com/actions/runner/releases/latest/download/actions-runner-linux-x64.tar.gz
tar xzf actions-runner-linux-x64.tar.gz
./config.sh --url https://github.com/$REPO_OWNER/$REPO_NAME \
--token $REG_TOKEN \
--name $RUNNER_NAME \
--labels $LABELS \
--unattended
sudo ./svc.sh install
sudo ./svc.sh start
Important : To provide the script execute permission:
chmod +x get-runner-token.sh
Step 5: Terraform Code (main.tf)
provider "azurerm" {
features {}
}
provider "local" {}
resource "null_resource" "fetch_runner_token" {
provisioner "local-exec" {
command = "bash ./get-runner-token.sh ${var.github_pat} > token.txt"
}
}
data "local_file" "runner_token" {
depends_on = [null_resource.fetch_runner_token]
filename = "${path.module}/token.txt"
}
output "runner_token" {
value = data.local_file.runner_token.content
sensitive = true
}
resource "azurerm_resource_group" "runner_rg" {
name = "runner-rg"
location = "UAE North"
}
resource "azurerm_virtual_network" "vnet" {
name = "runner-vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.runner_rg.location
resource_group_name = azurerm_resource_group.runner_rg.name
}