Terraform + Terragrunt project that declaratively manages the DivitMittal and Qezta GitHub organisations' repositories and branch protections via the GitHub provider. terranix/orgs/<Org>.nix files are the source of truth for repository definitions. A Nix flake provides the devshell, formatters, pre-commit hooks, Terranix JSON generation, and CI workflow generation.
ghOrg-terraform/
├── root.hcl # shared Terragrunt backend/version generation
├── orgs/ # per-org Terragrunt units
├── terranix/orgs/ # canonical org repo maps — edit here
├── modules/
│ ├── repositories/
│ │ ├── locals.tf # repo map (passed from root)
│ │ ├── main.tf # github_repository resource + check block
│ │ └── outputs.tf # node_ids, repo_urls, counts
│ └── branch-protection/
│ ├── locals.tf # protected_branches map
│ ├── variables.tf # repo_node_ids input
│ ├── main.tf # github_branch_protection resource
│ └── outputs.tf # protected_branch_rules
└── flake/ # Nix: devshell, formatters, pre-commit, Terranix, CI generation
└── actions/ # source for .github/workflows/*.yml
- Single source of truth: all repo definitions live in
terranix/orgs/<Org>.nix; Terragrunt generateslocals.tf.jsonfor Terraform. - Fork policy: fork repositories are intentionally unmanaged; omit forks from Terranix repo maps.
- Module wiring:
repositoriesoutputsnode_ids;branch_protectionreceives them viavar.repo_node_ids— avoids cross-module resource references. - Import blocks (Terraform ≥ 1.7): pre-existing repos are adopted in-place, never recreated.
prevent_destroy = true: every repository resource is guarded against accidental deletion.checkblock: validates all active repos have descriptions before any apply.movedblocks: migrate state from old flat root addresses to module addresses — safe to remove after first apply.- Generated CI: workflows in
.github/workflows/are rendered fromflake/actions/— do not edit by hand.
- Nix with flakes enabled
GITHUB_TOKENenvironment variable set with appropriate org permissions
Enter the Nix devshell (provides terraform, gh, nixd, alejandra):
nix develop
# or automatically via direnv:
direnv allow# Plan changes inside one org
cd orgs/DivitMittal # or orgs/Qezta
terragrunt plan
# Apply changes
terragrunt apply
# Format all Nix and Terraform files
nix fmt
# Run pre-commit checks (formatters, secret detection, etc.)
nix flake check
# Regenerate .github/workflows/ from flake/actions/
nix run .#render-workflows- Add an entry to
locals.reposinterranix/orgs/<Org>.nix:
"my-new-repo" = {
description = "What this repo is for";
visibility = "public";
topics = ["nix" "example"];
delete_branch_on_merge = true;
existing = false; # omit or set true for pre-existing repos
};-
If adopting a pre-existing repo, add a corresponding
importblock inmain.tf(or rely on thefor_eachimport block for repos whereexisting = true). -
If branch protection is needed, add the repo + branch to
locals.protected_branchesin the same org Terranix file. -
Verify and apply:
terragrunt plan
terragrunt applyGitHub Actions workflows are generated from Nix (flake/actions/):
| Workflow | Trigger | Purpose |
|---|---|---|
flake-check |
push/PR touching flake.nix or flake.lock |
Runs nix flake check |
flake-lock-update |
weekly schedule | Auto-updates Nix inputs via DeterminateSystems/update-flake-lock |