Skip to content

Automating Azure private networking for GitHub-hosted Runners with PowerShell, Bicep and GitHub CLI. Up and running in less than one minute!

License

Notifications You must be signed in to change notification settings

matsest/gh-runner-az-private-network-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

58 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Azure Private Networking for GitHub-hosted Runners Demo

This repository provides an end-to-end automated deployment of Azure and GitHub resources using PowerShell, Bicep and GitHub CLI - all in less than one minute! ⚑

Using GitHub-hosted runners within Azure VNET allows you to perform the following actions.

  • Privately connect a runner to resources inside an Azure VNET without opening internet ports, including on-premises resources accessible from the Azure VNET.
  • Restrict what GitHub-hosted runners can access or connect to with full control over outbound network policies.
  • Monitor network logs for GitHub-hosted runners and view all connectivity to and from a runner.

See this for more details about how the integration between GitHub and Azure works. Additional details is available in this repo in the learn more section.

Tip

This repo has been significantly updated to support the new GitHub API's allowing for full end-to-end automated deployment. You can check out the previous (still functional but not end-to-end automated) version see v1 here. (Run git checkout v1 after cloning.)

Prerequisites

  • An Azure subscription with Contributor and Network Contributor permissions (least privilege) or Owner permissions
  • An Team or Enterprise Cloud GitHub organization with organization Owner role (required to run operations via GH CLI with Oauth scopes)
    • Working on identifying if a lesser-privileged approach is supported, either using Oauth scopes, GitHub Apps or fine-grained tokens (awaiting discussion)
    • If you have a newer GitHub organization you will need to edit the default budget for Actions to more than $0. See this for more details on costs.
  • GitHub CLI (tested with 2.68.1)
  • PowerShell 7.x with Azure PowerShell modules (tested with PowerShell 7.5.0 and Az.Resources 7.8.1)
  • Azure Bicep (tested with 0.33.93)

Note that there is limited support for Azure regions with Azure Private Networking. See supported Azure regions here.

Usage

  1. Authenticate with GitHub CLI by running:
# Login
gh auth login -s admin:org,write:network_configurations

# If already logged in refresh scopes by running:
gh auth refresh -h github.com -s admin:org,write:network_configurations
  1. Authenticate with Azure PowerShell by running:
# Login
Connect-AzAccount
# Set context to subscription
Set-AzContext -Subscription <subscription name or id>
  1. Deploy Azure and GitHub configuration:

Option 1: Sandbox deployment: Run the following script to create a new resource group, a new virtual network and configure a new subnet:

./deploy.ps1 -GitHubOrganization <github org name>

Option 2: Deploy to existing vnet: Run the following script to create a new subnet in an existing virtual network and resource group:

$vnet = Get-AzVirtualNetwork -ResourceGroupName <rg name> -Name <name>

./deploy.ps1 -GitHubOrganization <github org name> `
    -Vnet $vnet `
    -SubnetAddressPrefix <address prefix> `
    -SubnetName <subnet name>

What will be deployed?

  • Azure:
    • Sandbox deploy: resource group, vnet with subnet, NSG and network settings
    • Existing vnet: subnet, NSG and network settings
  • GitHub (all configurations will be named after the vnet name):
    • Hosted Compute Networking Configuration
    • Runner Group (only available to private repositories)
    • Runner (Ubuntu 24.04, 2-core)

Example output

--------------------------------------------------------------------------------

πŸš€ Deploying GitHub-hosted runners with Azure Private Networking

Using GitHub organization '<org name>'
Using Azure subscription '<sub name>'
Running in sandbox mode - will deploy everything into a new resource group

--------------------------------------------------------------------------------

- Registring GitHub.Network resource provider...
    - Provider already registered
- Configuring resource group and virtual network...
- Deploying Azure subnet configuration...
    - Configured subnet: github-runner
- Creating GitHub hosted networking configuration...
    - Created networking configuration: gh-private-vnet
- Creating GitHub runner group...
    - Created runner group: gh-private-vnet
- Creating GitHub runner...
    - Created runner: gh-private-vnet-ubuntu-24.04

βœ… Deployment complete!

Deployment for Azure and GitHub completed in: 0m50s

--------------------------------------------------------------------------------

πŸ”— Link to Azure resource group:
https://portal.azure.com/<tenant id>/resource/subscriptions/<sub id>/resourceGroups/gh-private-runners

πŸ”— Link to GitHub hosted compute networking configuration:
https://github.com/organizations/<org name>/settings/network_configurations/<id>

πŸ”— Link to GitHub runner group with runner:
https://github.com/organizations/<org name>/settings/actions/runner-groups/<id>

πŸ’‘ Add the following to a GitHub Actions workflow to get started:

.github/workflows/az-private-networking-demo.yml:
---

name: az-private-networking-demo
on: [push]
jobs:
  demo:
    runs-on:
      group: gh-private-vnet
    steps:
      - uses: actions/checkout@v4
      - name: Show local IP address
        run: hostname -I

Clean-up

GitHub

Done via GitHub.com (in order):

  1. Delete the runner (might take a few minutes)
  2. Delete the runner group
  3. Delete the hosted networking configuration

Azure

Full deploy option

# Remove the resource group with all resources
Remove-AzResourceGroup -Name <name>

Existing vnet deploy option:

$resourceGroupName = <name>
$vnetName = <name>
$subnetName = <name>
$nsgName = <name>
$networkSettingsName = <name>

# Delete subnet
$vnet = Get-AzVirtualNetwork $vnetName -ResourceGroupName $resourceGroupName
Remove-AzVirtualNetworkSubnetConfig -Name $subnetName -VirtualNetwork $vnet | Set-AzVirtualNetwork

# Delete NSG
Remove-AzNetworkSecurityGroup -Name $nsgName -ResourceGroupName $resourceGroupName

# Delete network settings
Remove-AzResource -Name $networkSettingsName `
  -ResourceType 'GitHub.Network/networkSettings' `
  -ResourceGroupName $resourceGroupName
  -ApiVersion '2024-04-02'

Learn more

Cost

There will be a minimal Azure-related cost for network traffic depending on your setup, but the main cost of these runners will be the billing for the runners which is listed here. Billing is only counted when workflows are running - there is no idle cost for this solution.

Note that included GitHub Actions minutes for GitHub Team/Enterprise Cloud does not apply to larger runners, so all usage will be billed per-minute according to the rates linked above.

Examples:

  • One 2-core Linux runner running a 3 minute job 10 times a day: $0.008/min * 3 min * 10 = $0.24 per day
  • One 4-core Linux runner running a 5 minute job 10 times a day: $0.016/min * 5 min * 10 = $0.80 per day
  • Total of 50,000 minutes on 2-core Linux runners per month: $0.008/min * 50,000 = $400 per month ($12.9 per day)

Subnet size and runner concurrency

Based on GitHub documentation it's recommended to add a 30% buffer to the maximum job concurrency you anticipate. This needs to be taken into account when choosing the subnet size (Azure) and the maximum count of runners (GitHub) in the setup. Note that Azure reserves five of the IP addresses in a given subnet.

The relationship between a subnet address prefix and maximum number of runners it can hold can be calculated with the Convert-SubnetSizeToRunnersCount function which is used in the script to automatically resolve the count of runners to allow for.

Convert-SubnetSizeToRunnersCount "10.0.0.0/24" -Verbose
VERBOSE: Number of usable IPs: 251
VERBOSE: Maximum number of runners: 193
193


Convert-SubnetSizeToRunnersCount "10.0.0.0/24" -Verbose
VERBOSE: Number of usable IPs: 251
VERBOSE: Maximum number of runners: 193
193

Example values for common subnet sizes (/28 is the smallest useful subnet):

Subnet size IP addresses Usable IP addresses Max recommended # of runners
/28 16 11 8
/27 32 27 20
/26 64 59 45
/25 128 123 94
/24 256 251 193
/23 512 507 390

GitHub Static IP not supported

A static public IP from GitHub is not supported for privately networked runners. To gain a static egress IP for internet-bound traffic you will need to use an Azure Firewall, a NAT Gateway or a Load Balancer. Read more about Azure outbound connectivity methods here.

Please note that default outbound access will not be supported for new Azure subnets after September 30, 2025.

Filtering traffic by FQDN via a Firewall

If you are deploying into an existing vnet with a default route to a firewall that filters traffic (e.g. Azure Firewall) you will can whitelist these URL's to allow traffic from the runner to GitHub.

Optionally you kan simplify the outbound NSG-rules to only allow traffic to 'Internet' with an explicit rule and handle the granular filtering based on FQDNs in firewall rules. This is also normally allowed in NSGs as part of the default security rules, unless overridden with deny rules.

Runner image and installed software

This demo only supports Ubuntu images.

Currently Linux x64 and Windows x64 images is officially supported with Azure Private Networking. Images are automatically updated weekly and available software with versions is documented here. Images can be found from the API via the Get-GitHubOwnedImage function.

Support for macOS images is on the roadmap with an uncertain timeline, together with support for custom images.

Other options

If you are considering running runners for GitHub Actions in your own Azure private networking, and this scenario does not suit you, you can also consider:

Official documentation

License

MIT License

About

Automating Azure private networking for GitHub-hosted Runners with PowerShell, Bicep and GitHub CLI. Up and running in less than one minute!

Topics

Resources

License

Stars

Watchers

Forks