https://github.com/nolte/terraform-github-bootstrap
Bootstrap the nolte GitHub org via Terraform: org settings, teams, branch protection rulesets.
https://github.com/nolte/terraform-github-bootstrap
bootstrap github iac nolte terraform
Last synced: 11 days ago
JSON representation
Bootstrap the nolte GitHub org via Terraform: org settings, teams, branch protection rulesets.
- Host: GitHub
- URL: https://github.com/nolte/terraform-github-bootstrap
- Owner: nolte
- Created: 2026-05-20T17:47:42.000Z (about 1 month ago)
- Default Branch: develop
- Last Pushed: 2026-05-20T21:51:41.000Z (about 1 month ago)
- Last Synced: 2026-05-20T23:56:45.537Z (about 1 month ago)
- Topics: bootstrap, github, iac, nolte, terraform
- Language: HCL
- Homepage: https://github.com/nolte/terraform-github-bootstrap
- Size: 40 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# terraform-github-bootstrap
> Manage the [`nolte`](https://github.com/nolte) GitHub account as Terraform: repository inventory and repository rulesets.
## Purpose
This repository owns parts of the [`nolte`](https://github.com/nolte) GitHub configuration as code, using the [`integrations/github`](https://registry.terraform.io/providers/integrations/github/latest/docs) Terraform provider.
> **`nolte` is a personal user account on GitHub, not an organisation.** Organisation-only concerns (org settings, teams, organisation-wide rulesets) are therefore out of scope here. If `nolte` is migrated to an organisation later, those concerns can be added in a separate root module without disturbing the current code.
It is a deliberate complement to [`nolte/gh-plumbing`](https://github.com/nolte/gh-plumbing):
| Concern | Source of truth |
|---|---|
| Repository **inventory** (which repos exist, description, topics, visibility, has_issues / has_wiki / …) | **this repo** (Terraform `github_repository`) |
| Per-repo **repository rulesets** (modern branch protection) | **this repo** (Terraform `github_repository_ruleset`) |
| Per-repo settings, labels, merge strategy, classic branch protection | [`gh-plumbing`](https://github.com/nolte/gh-plumbing) (Probot Settings App via `_extends`) |
| Reusable workflows, Boring-Cyborg / Stale / Release-Drafter commons, Renovate preset | [`gh-plumbing`](https://github.com/nolte/gh-plumbing) |
To keep both systems from fighting, the `github_repository` resource here `ignore_changes` on every field Probot owns (merge strategies, `delete_branch_on_merge`, auto-merge).
## Getting started
```sh
# Install pinned tool versions (see .tool-versions).
task -l # discover available targets
task tf:fmt # format
task tf:validate # validate every root module
task tf:plan # plan against the live account (requires GITHUB_TOKEN with `repo` scope)
task tf:apply # apply (human-gated)
```
`terraform.tfvars` lives next to the root module and is git-ignored — copy `terraform.example.tfvars` and adjust. **Adopt every existing repo via `terraform import github_repository.managed[""] ` before the first apply**, otherwise Terraform will try to create a duplicate.
State backend is currently **local** (`terraform.tfstate` next to the module). When a remote backend is adopted, update the module and migrate state explicitly.
## Structure
```
terraform/
repos/ # repository inventory + per-repo rulesets
portfolio-app/ # wrapper around gh-plumbing//terraform/portfolio-app
# provisions per-repo PORTFOLIO_APP_ID + PORTFOLIO_APP_PRIVATE_KEY
docs/ # MkDocs source (English)
.github/ # Probot configs + reusable-workflow consumers
scripts/ # operator helpers (gopass → TF_VAR_* env loaders)
```
### portfolio-app credentials
The portfolio-app module needs a GitHub App that is **registered manually** — there is no GitHub API for App creation or private-key generation. Five manual steps before the first `task tf:apply:portfolio-app`:
1. **Register the App** at . Permissions and webhook settings are documented at [`docs/en/portfolio-app.md`](docs/en/portfolio-app.md) and in the [upstream setup guide](https://github.com/nolte/gh-plumbing/blob/develop/docs/en/portfolio-app/setup.md).
2. **Generate a private key** (`.pem`) on the App's settings page.
3. **Note the numeric App ID** (visible on the App's settings page).
4. **Install the App** in every consumer repository (default: `terraform-github-bootstrap`, `gh-plumbing`, `claude-shared`).
5. **Persist credentials in gopass** and let Terraform read them via `TF_VAR_*`:
```sh
GP=internet/github.com/nolte/apps/nolte-portfolio-app
gopass insert "$GP/appid" # numeric App ID
gopass insert "$GP/slug" # e.g. nolte-portfolio-app
gopass insert -m "$GP/private_key" < downloaded.pem
shred -u downloaded.pem
# Each session before plan/apply:
source scripts/portfolio-app-env.sh
task tf:plan:portfolio-app
```
`scripts/portfolio-app-env.sh` exports `TF_VAR_app_id`, `TF_VAR_app_private_key`, and `GITHUB_TOKEN`; nothing touches tfvars or the repo. Full operator runbook at [`docs/en/portfolio-app.md`](docs/en/portfolio-app.md).
> **Spec note.** `spec/project/project-structure/` does not currently list `terraform/` as a sanctioned top-level source tree (only `src/`, `custom_components/`, `.claude-plugin/`, `playbooks/`+`roles/`). This repository uses `terraform/` as a conscious extension — analogous to the Ansible exception. Tracked under `spec/source-layout-extension.md` once the spec amendment lands upstream.
## Related repositories
- [`nolte/gh-plumbing`](https://github.com/nolte/gh-plumbing) — per-repo Probot Settings, reusable workflows, Renovate preset.
- [`nolte/claude-shared`](https://github.com/nolte/claude-shared) — Claude Code plugin used to scaffold this repository.
## Status
Bootstrap / pre-MVP. Local state, no CI gate on `tf:plan` yet. Repository inventory starts with this bootstrap repo itself (dogfood) and is meant to grow one `terraform import` at a time.
## License
To be added. See open question in `spec/`.