You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Jul 10, 2025. It is now read-only.
* docs: update readme to reflect updatated quota name form 'Low Priority' to 'Spot'
* docs(readme): expand architecture details and component docs
* Added “How It Works” section outlining event flow from GitHub to VM teardown
* Documented roles of Service Bus, App Configuration, both Key Vaults, and Shared Image Gallery
* Clarified Runner-Controller responsibilities and queue consumption behavior
Closes#109
* docs: Add information notes in the docs for the different azure keyvault
authentication methods depending RBAC vs Access Policy.
Also includes a simple example to help get users started.
1.**GitHub Actions event ➜ Event-Handler** – A `workflow_job` event is emitted by GitHub Enterprise/Cloud and forwarded by a GitHub App to the Event-Handler Azure Function.
21
+
2.**Event-Handler ➜ Service Bus** – The function validates the request, filters on runner labels, and publishes a message to an Azure Service Bus queue.
22
+
3.**Service Bus ➜ Runner-Controller** – The Runner-Controller Azure App Service continuously listens to the queue. Based on settings stored in Azure App Configuration it decides whether to spin up a new runner or rely on the warm-pool.
23
+
4.**Runner-Controller ➜ Azure VM** – When a new runner is required the controller:
24
+
• Fetches the latest Packer-built image from an Azure Shared Image Gallery.
25
+
• Retrieves a one-time registration token from GitHub, storing it in the Registration Key Vault.
26
+
• Creates a spot/on-demand VM, injecting the token via cloud-init so the VM registers itself with GitHub on first boot.
27
+
5.**Runner lifecycle** – The VM processes exactly one job, then a shutdown hook notifies the controller which de-registers the runner and deletes the VM, keeping the warm-pool at the desired size.
28
+
6.**Secrets & config** – GitHub App credentials and webhook secret live in the App Key Vault; operational parameters are stored in Azure App Configuration. All resources use Managed Identities for least-privilege access.
29
+
7.**Provisioning** – This entire architecture is provisioned reproducibly by the Terraform module contained in this repository.
30
+
31
+
19
32
## Components
20
33
21
34
### Terraform Module
@@ -25,15 +38,31 @@ This [Terraform](https://www.terraform.io/) module generates the infrastructure
25
38
The event-handler will receive messages from the GitHub App during workflow run events. It will act as a filter to ensure they are from GitHub with labels that match what is provided in the module.
26
39
27
40
### Runner-Controller (Azure App Service)
28
-
This application will act as the controller for the warm pool and ensure that the pool size adheres to the parameters specified in the Terraform module. It will consume events from the queue as necessary to create VMs and ensure a healthy number of VMs are always ready to process new workflow jobs.
41
+
This application will act as the controller for the warm pool and ensure that the pool size adheres to the parameters specified in the Terraform module. It consumes Service Bus messages to create or delete VMs so that a healthy number of runners are always ready to process workflow jobs.
42
+
43
+
### Service Bus (Azure Service Bus)
44
+
A reliable message queue that buffers `workflow_job` events between the Event-Handler and Runner-Controller, ensuring no job is lost and enabling smooth scale-out.
45
+
46
+
### App Configuration (Azure App Configuration)
47
+
Central store for runtime settings such as warm-pool size, VM SKU, image version, and spot/on-demand preferences. Runner-Controller reads these values on startup and on a configurable interval.
48
+
49
+
### App Key Vault (Azure Key Vault)
50
+
Holds long-lived secrets used by the Event-Handler and Runner-Controller (GitHub App private key, webhook secret, etc.). Accessed via Managed Identity with the minimum required permissions.
51
+
52
+
### Registration Key Vault (Azure Key Vault)
53
+
Stores the short-lived registration tokens generated by Runner-Controller. Each token is valid for a single VM and is deleted once the runner registers with GitHub.
54
+
55
+
### Shared Image Gallery
56
+
Contains HashiCorp Packer-built VM images pre-loaded with the toolchain your organization needs. Runner-Controller always provisions the latest image version.
57
+
29
58
30
59
## Getting Started
31
60
32
61
### Pre-requisites
33
62
- GitHub App for Organization (owner access)
34
63
- Azure
35
64
- Subscription
36
-
-*Note: Subscription quota for "Total Regional Low-priority vCPUs" should be increased to allow multiple spot instances*
65
+
-*Note: Subscription quota for "Total Regional Spot vCPUs" should be increased to allow multiple spot instances*
37
66
- Resource Group
38
67
- Subnet with internet access
39
68
- KeyVault for GitHub App Credential
@@ -71,11 +100,11 @@ The GitHub App serves as the foundation for sending webhook events to App A and
71
100
| Homepage URL | {insert-any-url} |
72
101
| Webhook Active | False |
73
102
| Webhook URL ||
74
-
| Subscribe to events | Workflow job |
103
+
| Subscribe to events*| Workflow job |
75
104
| Where can this GitHub App be installed? | Only on this account |
76
105
77
106
*Note: You will need one GitHub App per org. Allowing installation to "Any account" makes it difficult to change access if installed on orgs outside your control.
78
-
107
+
*Note: Initially the webhook is disabled, but will be enabled in the next step. You will only see 'Subscribed to events' after the webhook is enabled.
79
108
#### **Add secrets to Azure KeyVault**
80
109
81
110
(optional) Set Key Vault name variable:
@@ -108,34 +137,119 @@ Webhook Secret:
108
137
az keyvault secret set --name github-webhook-secret --value $(uuidgen) --vault-name $KEYVAULT_NAME
109
138
```
110
139
111
-
*Note: The private key must be added via the [AZ CLI](https://learn.microsoft.com/en-us/cli/azure/), all other secrets can be added manually via the portal if you choose to do so.
140
+
*Note: The private key must be added via the [AZ CLI](https://learn.microsoft.com/en-us/cli/azure/), all other secrets can be added manually via the portal if you choose to do so.*
141
+
142
+
> **Key Vault authorization models**
143
+
>
144
+
> • If your Key Vault was created with **RBAC authorization** (`enableRbacAuthorization = true` or `az keyvault create --enable-rbac-authorization true`), you must grant the **Key Vault Secrets Officer** (or Administrator) role to **both** the Event-Handler Function **and** the Runner-Controller App Service managed identities. Terraform cannot assign this role unless you opt into the upcoming RBAC support variable.
145
+
>
146
+
> • If your vault uses the older **access-policy model** (`enableRbacAuthorization = false`, the module default), Terraform will automatically create an access policy that lets the Function and Controller read secrets.
147
+
>
148
+
> **Default behaviour:** The Azure portal and recent versions of the Azure CLI (*az* ≥ 2.51) create Key Vaults with **RBAC enabled by default**. In contrast, the Terraform `azurerm_key_vault` resource keeps **RBAC disabled by default**—you must set `enable_rbac_authorization = true` to opt-in. Keep this in mind when mixing manual and IaC-provisioned environments.
149
+
112
150
113
151
### Setup Terraform Module
114
152
115
-
Consume this `azure_github_runner` module with inputs required for your GitHub Enterprise Cloud or GitHub Enterprise server configuration. Example of how to consume the module are coming soon.
153
+
Below is a **minimal working example** you can copy-paste into a new `main.tf`. Replace the placeholder values with your own IDs.
154
+
155
+
```hcl
156
+
terraform {
157
+
required_providers {
158
+
azurerm = {
159
+
source = "hashicorp/azurerm"
160
+
version = ">= 3.100"
161
+
}
162
+
azuread = {
163
+
source = "hashicorp/azuread"
164
+
version = ">= 2.45"
165
+
}
166
+
}
167
+
}
168
+
169
+
provider "azurerm" {
170
+
features {}
171
+
tenant_id = "00000000-0000-0000-0000-000000000000" # Azure AD tenant
owners = ["00000000-0000-0000-0000-000000000000"] # Azure AD object IDs
202
+
}
203
+
204
+
output "function_webhook_url" {
205
+
value = module.github_runners.function_webhook_url
206
+
description = "URL to paste into the GitHub App webhook settings"
207
+
sensitive = true
208
+
}
209
+
```
116
210
117
-
Run terraform by using the following commands
211
+
Run Terraform:
118
212
119
-
```zsh
213
+
```bash
120
214
terraform init
121
-
terraform apply
215
+
terraform plan -out tfplan
216
+
terraform apply tfplan
217
+
```
218
+
219
+
After `terraform apply` completes, run:
220
+
221
+
```bash
222
+
terraform output -raw function_webhook_url
122
223
```
123
224
124
-
The terraform output displays the Azure Function endpoint and secret, which you need in the next step.
225
+
This returns **the full Azure Function URL**, including a `?code=` query-string. Copy the **entire string** – the `code` value is the Function *host key* and must stay in the URL.
226
+
227
+
You will also need the **GitHub webhook secret** that you stored in Key Vault earlier:
228
+
229
+
```bash
230
+
az keyvault secret show \
231
+
--vault-name <your-kv-name> \
232
+
--name github-webhook-secret \
233
+
--query value -o tsv
234
+
```
235
+
236
+
Keep both values handy for the next step.
125
237
126
238
### Deploy Function App and App Service
127
239
128
240
This terraform module is set up by default to use the latest version of both apps and deploy them on `terraform apply`. Specific versions found in our public [GitHub Packages](https://github.com/orgs/liatrio/packages?repo_name=terraform-azure-github-runner) and set in the terraform module inputs. If you choose to publish your own images, functionality to do so will be implemented soon™.
129
241
130
-
### Setup the webhook and install the GitHub App
242
+
### Configure the GitHub App webhook and install the app
243
+
244
+
In the GitHub UI navigate to **Settings → Developer settings → GitHub Apps → _Your App_** and:
131
245
132
-
Go back to the GitHub App and update the following settings
246
+
1.**Activate** the webhook toggle.
247
+
2.**Webhook URL** – paste the Function URL you copied from `terraform output` (it already contains the `code` host key).
248
+
3.**Webhook secret** – paste the value of `github-webhook-secret` you retrieved from Key Vault.
249
+
4.**Save**.
250
+
5. Open **Install App**, select the gear icon next to your organization, under 'Repository access' select 'All repositories' and click **Save**.
133
251
134
-
1. Activate the webhook
135
-
2. Provide the webhook url, which should be part of the terraform output
136
-
3. Provide the webhook secret
137
-
4. Save changes and navigate to the Install App tab
138
-
5. Next to your GitHub App, select Install next to your org and select 'All Repositories'
252
+
The system is now wired together: GitHub sends signed workflow-job events to your Function, the Function enqueues work, and the Runner Controller spins up VMs on demand.
0 commit comments