Skip to content

Prepare GitHub (GitHub Variant)

Use this page when the customer's repositories and pipelines are on GitHub. The phases are the same as for Azure DevOps. Only the preparation differs: a GitHub App instead of tokens, federated credentials instead of service connections with secrets, environments instead of Azure DevOps environments, and a different way to get self-hosted runners.

Topic Azure DevOps GitHub
Sign in to Azure Service connection (Prepare identity) Service principal with federated credentials (OIDC), described below
Check out the code repository Service connection to the host (mcp-code) GitHub App with a short-lived installation token
Production approval Environment with approvers Environment with required reviewers
Runners Managed DevOps Pool (Prepare agents) See Runners
Repository setup Prepare repositories The same page, GitHub sections

Prerequisites on the organization

The platform team needs the organization roles CI/CD admin and App Manager, and must be able to create repositories. See Prerequisites and the GitHub checks there.

GitHub App for the workflows

The workflows do not use personal access tokens. They create a short-lived installation token for a GitHub App (actions/create-github-app-token) and pass it to Terraform (TF_VAR_github_token).

  1. Create a GitHub App in the organization, or reuse one. Give it a name and description that say it is used by the platform workflows.
  2. Repository permissions: read access to code and metadata, read and write access to workflows.
  3. Store the App ID as the organization variable cloud_platform_gh_app_id and the private key as the organization secret cloud_platform_gh_app_private_key (Pipelines).
  4. Install the app in the organization with Only select repositories, and select only the code repositories.
  5. Store a copy of the private key in the secret store of the platform team, together with the app name and ID.

If spokes use the GitHub extension, the app needs a larger set of permissions (Prepare spoke integrations). When Terraform modules are downloaded from a GitHub Enterprise host, see Module source access.

This is a different app from the one that syncs the code repository (Prepare repositories). You may combine them if your security rules allow it, but separate apps keep the permissions small.

When mcp.json has a GitHub section (AppId, AppInstallationId, AppPrivateKeySecretName), it refers to the GitHub App that registers the self-hosted runners (Config).

Federated credentials

With federated credentials the workflow signs in to Azure without a stored secret. Add three credentials to the deployment service principal (Microsoft Entra ID, App registrations, the app, Certificates & secrets, Federated credentials, Add credential, scenario GitHub Actions deploying Azure resources) for each configuration repository:

Credential Entity type Subject identifier
Branch Branch repo:<organization>/<repository>:ref:refs/heads/main
Production Environment, prod repo:<organization>/<repository>:environment:prod
Canary Environment, canary repo:<organization>/<repository>:environment:canary
  • Issuer. For GitHub.com it is https://token.actions.githubusercontent.com. For GitHub Enterprise on a dedicated domain (<domain>.ghe.com) it is https://token.actions.<domain>.ghe.com. The organization and repository names are the ones in the repository URL.
  • During the implementation, when the default branch is initial, create the branch credential for initial. Change it to main when the implementation is done (Prepare repositories).
  • Put the client ID of the service principal in Azure.TerraformState.Hub.ServicePrincipalId (and .Spoke.) in mcp.json.

Secrets in older workflows

Older entry workflows (main.yaml) read these repository secrets instead of mcp.json:

Secret Value
AZURE_CLIENT_ID Client ID of the service principal
AZURE_TENANT_ID Tenant ID
AZURE_SUBSCRIPTION_ID Management subscription of the platform environment
AZURE_RG_NAME, AZURE_SA_NAME Resource group and storage account of the Terraform state
AZURE_SA_CONTAINER_NAME State container (a different container is used for spokes)
AZURE_SA_LOG_CONTAINER_NAME Container for deployment logs

Environments

Create the environments canary and prod in each configuration repository, plus approval-required if you use the Approve* modes (Pipelines). For prod and approval-required:

  • required reviewers: the approver group, which must have read access to the repositories,
  • Prevent self-review on,
  • Allow administrators to bypass configured protection rules off.

Without reviewers on approval-required the approval step continues without waiting.

Runners

Current approach

The platform's infrastructure and container workflows deploy the job runners, as for Azure DevOps (Prepare agents). The runner registration uses the GitHub App in mcp.json. The deployment of the hub runs on those runners.

Older approach: GitHub-hosted runners with virtual network integration

Earlier versions deployed the hub in two phases with a ghrunners layer. The layer is not part of the current hub pipeline; this section is for tenants that still run the older workflows.

  1. Phase 1, default runners. The first layers (foundation, connectivity, ghrunners) run on a standard GitHub-hosted runner group (a runner on the current Ubuntu image, for example 2 cores, 8 GB, 75 GB disk), because no network exists yet. Point defaultRunnerGroup in .github/workflows/main.yaml to that group.
  2. Phase 2, virtual-network-integrated runners. Once the network exists, create runners that live in it:
    1. Get the database ID of the organization or enterprise:

      gh auth login -s 'read:enterprise'
      gh api graphql -f query='query($login: String!) { organization(login: $login) { login databaseId } }' -f login='<organization>'
      

      For an enterprise, query enterprise (slug: $slug) instead. For a ghe.com host, add --hostname <subdomain>.ghe.com. 2. In the connectivity subscription, register the GitHub.Network resource provider, delegate the runner subnet to GitHub.Network/networkSettings, and create the network settings resource (type GitHub.Network/networkSettings, with the subnet ID and the database ID as businessId). Do this for canary and for prod. Use a region that GitHub-hosted runner networking supports (the runbook used North Europe because West Europe was not supported at that time; check the current list). 3. In the organization settings, Hosted compute networking, create a network configuration for each platform environment with the network settings resource ID. If the option is disabled, the enterprise owner must enable it. If the form returns a JSON error, wait a few minutes and retry. 4. Create a runner group per environment (for example lz-canary and lz-prod) with that network configuration and access for the four repositories, and in each group a GitHub-hosted runner. Wait until the runner status is Ready. 3. Switch defaultRunnerGroup in main.yaml to the new group, then run the remaining layers in order: firewall, identity, management, postdeployment. 4. Add firewall rules so the runners can reach what they need, as in Network access of the agents.

See also Deploy the platform.