I'm in the unlucky position to have to deal with GitHub. Thus I've a terraform module in a project which deals with populating organization secrets in our GitHub organization, and assigning repositories access to those secrets.
Since the GitHub terraform provider internally works mostly
with repository IDs, not slugs (this human readable
organization/repo
format), we've to do some mapping in between.
In my case it looks like this:
#tfvars Input for Module
org_secrets = {
"SECRET_A" = {
repos = [
"infra-foo",
"infra-baz",
"deployment-foobar",
]
"SECRET_B" = {
repos = [
"job-abc",
"job-xyz",
]
}
}
# Module Code
/*
Limitation: The GH search API which is queried returns at most 1000
results. Thus whenever we reach that limit this approach will no longer work.
The query is also intentionally limited to internal repositories right now.
*/
data "github_repositories" "repos" {
query = "org:myorg archived:false -is:public -is:private"
include_repo_id = true
}
/*
The properties of the github_repositories.repos data source queried
above contains only lists. Thus we've to manually establish a mapping
between the repository names we need as a lookup key later on, and the
repository id we got in another list from the search query above.
*/
locals {
# Assemble the set of repository names we need repo_ids for
repos = toset(flatten([for v in var.org_secrets : v.repos]))
# Walk through all names in the query result list and check
# if they're also in our repo set. If yes add the repo name -> id
# mapping to our resulting map
repos_and_ids = {
for i, v in data.github_repositories.repos.names : v => data.github_repositories.repos.repo_ids[i]
if contains(local.repos, v)
}
}
resource "github_actions_organization_secret" "org_secrets" {
for_each = var.org_secrets
secret_name = each.key
visibility = "selected"
# the logic how the secret value is sourced is omitted here
plaintext_value = data.xxx
selected_repository_ids = [
for r in each.value.repos : local.repos_and_ids[r]
if can(local.repos_and_ids[r])
]
}
Now if we do something bad, delete a repository and forget to remove it from the configuration for the module, we receive some error message that a (numeric) repository ID could not be found. Pretty much useless for the average user because you've to figure out which repository is still in the configuration list, but got deleted recently.
Luckily terraform supports since version
1.2 precondition checks, which we can use in an output
-block
to provide the information which repository is missing. What we
need is the set
of missing repositories and the validation condition:
locals {
# Debug facility in combination with an output and precondition check
# There we can report which repository we still have in our configuration
# but no longer get as a result from the data provider query
missing_repos = setsubtract(local.repos, data.github_repositories.repos.names)
}
# Debug facility - If we can not find every repository in our
# search query result, report those repos as an error
output "missing_repos" {
value = local.missing_repos
precondition {
condition = length(local.missing_repos) == 0
error_message = format("Repos in config missing from resultset: %v", local.missing_repos)
}
}
Now you only have to be aware that GitHub is GitHub and the TF provider has open bugs,
but is not supported by GitHub and you will encounter
inconsistent results. But
it works, even if your terraform apply
failed that way.