Terraforming GitHub Teams
Terraforming teams
I have spent some time digging into how to setup a GitHub team that can be used fully as a team, while being managed entirely through our TerraForm setup.
module
vs. resource
There are two ways to create a team, one is as a resource
as a
github_team
and the other is as a module
.
I want to use module
because that supports the source_file
directive
that we can use to refer to a CSV-file that contains all the teams
members, and avoid manually managing teams with the GitHub UI.
The steps required
Important New outputs must be added and applied before they are referenced in a file, so the minimum amount of PRs that are needed is two. Create the first after adding the
output
to./outputs.tf
.
- Add a CSV-file with the team members to
./teams/orgs/{team-name}.csv
- Add a team to
./teams.tf
- Add an output to
./outputs.tf
- Make sure there is a
terraform_remote_state
in the./*/backend.tf
file - Add the team to each repo that the team owns in
./*/main.tf
Important information about parent team
It is possible to use github_team.developers.id
as the
parent_team_id
, and that allows the team to be mentioned, assigned,
and used across all repos, which is good for teams where everyone
should have read and write access.
If it is true that all team members should have read/write, then the team does not need to be added to all the repos manually, and step 5 in the required steps can be skipped.
For teams where only some should have read/write, use
github_team.parent-amut-teams.id
and add the team only to repos
where it is OK for everyone to have read/write access.
Setting it up
So first up, let's create a CSV-file with our team members:
full_name,username,team_role,github_role
Pat the Bunny,bunny,maintainer,member
Tom Waits,waits,member,member
Nick Cave,cave,maintainer,admin
Most of us are common folk, so the github_role
should be member
.
Some people are admins, so makes sense to have them as admin
. I am not
sure what the consequences of messing this up are.
The team_role
though, can be customized a bit, with either member
or
maintainer
. In practice, a maintainer
can do things like change
team settings, such as pull request reminders, which is handy.
Next up, let's create the team in ./teams.tf
:
module "{team-name}" {
source = "./modules/org_team"
gh_token = var.gh_token
parent_team_id = github_team.parent-amut-teams.id
team_name = "{display name for the team}"
team_description = "{description of the team}"
source_file = "${path.module}/teams/org/{team-name}.csv"
}
Replace the {placeholders}
with real values.
The parent_team_id
(github_team.parent-amut-teams.id
) refers to the
team used to represent the organisation structure, so that makes sense
to use here.
We need to add an output
to the ./outputs.tf
file in order for us to
actually refer to the team by value in other places.
In the ./outputs.tf
file:
output "{team-name}" {
value = module.{team-name}
}
It is important that the module.{team-name}
placeholder is the same as
the one used when the module
was defined in ./teams.tf
:
module "{team-name}" {}
So if the module was named foobar
, the value for the output should be
module.foobar
.
Stage your changes to the files ./teams.tf
, ./outputs.tf
, and
./teams/org/{team-name}.csv
, and commit them. Push to a new branch and
create a pull request.[1]
It needs to be approved and applied before we continue with linking a repo to a team.
Once that is done, let's proceed.
Find the repo you want to link to the new team in ./*/main.tf
, and
modify it:
module "{repo-name}" {
source = "../modules/repos/"
gh_token = var.gh_token
gh_webhook_secret = var.gh_webhook_secret
name = "{repo name}"
description = "{repo description}"
teams = {
"developers" = data.terraform_remote_state.tf_github_common.outputs.developers.id
"{team-name}" = data.terraform_remote_state.tf_github_common.outputs.{team-name}.team_id
}
whoami = {
# lots of stuff
}
}
Add the teams
block, and add the developers
line as-is. It is the
default team that should always[2] have access to a repo.
The {team-name}
line gives the team you created access to the repo, so
it can be referenced as a member of the repo, in e.g. pull request
reviewers.
Note the
data.terraform_remote_state.tf_github_common.outputs.{team-name}.team_id
part.
This should refer to the name you gave the output
. The reason we use
team_id
is because we used a module
, and that exposes the team_id
variable.
developers
on the other hand is defined as a resource
, and that
exposes the id
variable.
The last check is to take a look at the ./*/backend.tf
, located next
to the ./*/main.tf
file you found your repo in, and make sure it has
the block:
data "terraform_remote_state" "tf_github_common" {
backend = "gcs"
config = {
bucket = "amedia-tf-github-state-prod"
prefix = "prod"
}
}
So if your repo was in ./p/main.tf
, the ./p/backend.tf
file must
contain the terraform_remote_state
block. If it is not there, you need
to add it, otherwise the output
will not be available in main.tf
.
Now, you can stage and commit your changes to the ./*/main.tf
and
./*/backend.tf
files. Push the changes to a new branch, and create a
PR.[3]
Once that PR is approved and applied, you can now use your team for reviews, create reminders, and whatever else GitHub comes up with.
/v.