GitHub estate as code
Data-driven Terraform applied to GitHub as two related concerns, so administrators manage the organisation as data, and anyone can get a properly set-up repository by pull request.
By: Benjamin Clark
Intent
This pattern is a specialised implementation of data-driven Terraform. It realises data-driven Terraform's core aim (shifting infrastructure left to the people who use it) within the management of a GitHub organisation, managed with the GitHub provider for Terraform. It assumes an organisation with more than a handful of repositories, and treats it as two separate but related concerns, each in a repository of its own:
- Organisation management, where administrators set the organisation's settings, its teams and the rules every repository follows.
- The repository factory, where anyone can request a new repository by pull request, and get it set up the same way as every other.
Keeping them apart lets each be owned, reviewed and changed by the right people. This enables three outcomes:
- Anyone can get a new repository by pull request, set up the same way as every other, without waiting for an administrator to create it.
- Administrators deliver faster, because they maintain the organisation's settings, access and rules from one place, rather than creating every repository themselves.
- Every repository and every change of access is reviewed and recorded, and changes made by hand are put back.
Motivation
When a GitHub organisation grows by hand, with people creating repositories and setting their protection in the browser, it fails in three ways:
- Nobody can say who has access to what. Access and protection are granted repository by repository and person by person, so the organisation's policy exists only as the sum of its settings.
- Repositories drift. Each one is created differently, with its own files, protection and access, and nobody can say which meet the organisation's standards.
- Administrators become a bottleneck. Every new repository and every change of access waits on the few people who can make it, and routine requests queue alongside decisions about policy.
This pattern separates the two concerns, because they differ in who changes them, how often, and how much a change can affect. Each lives in its own repository, with its own primitives (data-driven Terraform's name for each kind of resource a repository manages). Both use data-driven Terraform's colocated variant, keeping each primitive's module in the same repository as its data, because GitHub is a single service with nothing to version or share.
Only administrators change organisation management, and rarely, but one change can affect every repository at once. Its primitives typically include:
- The organisation's settings, such as one that stops members creating repositories by hand.
- Teams, which can take their members from the organisation's identity provider, such as Microsoft Entra ID or Okta, through team synchronisation, so GitHub access follows the organisation's own record of who works where. Without one, the data lists each person and the teams they belong to, because administrators think about which teams a person is in, rather than who is in a team.
- Organisation-wide roles, which give a team access to every repository at once.
- Rulesets that every repository follows, including repositories created later.
Anyone can change the repository factory's data tier, by pull request, and often, to request a repository, but each entry affects only the repository it describes. Only administrators change its infrastructure and instantiation tiers, which decide how every repository is set up. Each repository is one entry, with defaults for anything it leaves out, and the factory creates it with the same standard files, such as a licence and a code owners file naming who must review changes, and the same team access as every other repository.
The two concerns are related in three ways:
- Organisation management stops repositories being created by hand, so the factory is the only way to get one.
- The factory grants access to the teams organisation management defines, so a team is defined once, and every repository that names it follows any change to it.
- Organisation-wide rulesets cover every repository the factory creates, so the factory never sets branch protection repository by repository.
Administrators then become custodians of the organisation's settings, access and rules, and of how the factory sets up each repository, as infrastructure specialists do in data-driven Terraform, while everyone else serves themselves through the factory's data tier.
Each concern has a pipeline of its own, which plans every pull request and applies on merge. Each pipeline also applies again on a schedule, putting back any change made outside the code. That does two things:
- It combats configuration drift, where the estate gradually stops matching its code as people change settings by hand.
- It shortens how long an unauthorised change stays in place. A change of access or protection made by a malicious actor lasts only until the next scheduled apply, rather than until someone notices it.
Each pipeline signs in to GitHub as an application with an identity of its own, such as a GitHub App, holding only the permissions its concern needs, never with a person's token. The factory's application can create and configure repositories, but never change the organisation's settings, teams or rulesets, so a repository request can never change organisation policy.
The two variants below differ in how many organisations the estate holds.
Applicability
Use this pattern when:
- A GitHub organisation has more than a handful of repositories, and will be around for a while.
- Repositories, their protection and their access should be consistent, rather than set by hand.
- You need to say who has access to what, with every change reviewed.
Do not use this pattern when:
- The organisation is short-lived, with a handful of repositories, where setting up the factory takes more effort than it saves.
Structure
Variant A: One organisation
When the business works in a single GitHub organisation, this variant works best.
- One organisation management repository sets the organisation's settings, teams and rulesets.
- One repository factory creates every repository in it.
Variant B: An enterprise of organisations
When a GitHub Enterprise holds an organisation for each business unit, and a platform team must set the same baseline in all of them, this variant works best.
- Organisation management also creates the organisations themselves, within the enterprise, before anything inside them is set.
- A baseline applies in every organisation, such as rules that stop long-lived branches being deleted or overwritten, while fuller standards are opt-in for each organisation.
- Enterprise-wide teams, such as platform and security, join every organisation, while each business unit keeps its own teams and its own identity provider.
- The repository factory creates each repository in whichever organisation it belongs to.
Participants
| Participant | Role |
|---|---|
| Administrator | Changes organisation management and the repository factory's infrastructure and instantiation tiers, and reviews other administrators' changes to them. |
| Organisation repository | Holds organisation management as data, such as the organisation's settings, teams, organisation-wide roles and rulesets. |
| Organisation pipeline | Plans and applies organisation management, signing in as an application with the broad permissions that concern needs. |
| End user | Requests a repository by adding an entry to the repository factory, with no knowledge of Terraform needed. |
| Repository factory | Holds one entry per repository in its data tier, which anyone can change by pull request, and its infrastructure and instantiation tiers, which only administrators change. Together they create each repository with the same files, and access for the teams organisation management defines. |
| Factory pipeline | Plans and applies the repository factory, signing in as an application that can create and configure repositories, but never change organisation management. |
| Identity provider | Decides who is in each team, through team synchronisation, so access follows the organisation's own record of who works where. |
| GitHub organisation | Holds the repositories, teams and rulesets the code describes, and applies the rulesets to every repository. |
Constraints and trade-offs
- Every constraint of data-driven Terraform still applies, from strict schema discipline to mature state management.
- Organisation management has a wide blast radius. One change can affect every repository, so its pipeline's application needs broad permissions, kept with the same care as an administrator's credentials, and each change deserves the review of a change to production.
- Where teams take their members from the identity provider, membership leaves code review. Whoever manages a group there can grant GitHub access without a pull request, and team synchronisation is a feature of GitHub Enterprise Cloud that needs the identity provider's administrator (often in another team) to set up. Without it, membership stays in the data, and every change to it is reviewed.
- The concerns change in order. A team has to exist in organisation management before the factory can grant it access, so a new team and its first repository take two changes.
- A new repository waits for its pull request to be reviewed and applied, so it takes minutes rather than seconds. In return, it starts out set up to the organisation's standards, with its access reviewed and recorded, whereas a repository created in the browser in seconds still has to be set up by hand afterwards.
Variant A: One organisation trade-offs
- The core benefit is simplicity: one place for policy and access, and one place to request a repository.
- The core cost is that every team follows the same settings and rules, which suits one business but not several units that each need their own.
Variant B: An enterprise of organisations trade-offs
- The core benefit is one baseline everywhere, while each business unit keeps its own teams, identity provider and pace.
- The core cost is more moving parts: organisations have to exist before anything inside them can be set, and enterprise-wide settings reach every organisation at once.
Part of how I deliver
This is how I let teams get a properly set-up repository by pull request, while administrators keep control of access and policy. It's one of many patterns I bring through contract delivery, and it works for a single organisation or an enterprise of them.
Are your engineers waiting on infrastructure?
I build infrastructure and pipelines your engineers serve themselves from, with your standards enforced in one place. If releases are slow, risky or queued behind one specialist, book a call and we'll talk it through.
