Skip to content

Using as a Module

Marco edited this page Feb 19, 2026 · 1 revision

Instead of forking this repository, you can reference terraform/ as a reusable Terraform module. This separates the shared infrastructure logic from your per-org YAML configuration, so you only maintain config files and a short main.tf.

Minimal main.tf

terraform {
  required_version = ">= 1.0"
  required_providers {
    github = {
      source  = "integrations/github"
      version = "~> 6.0"
    }
  }
}

provider "github" {
  owner = "your-org-name"
}

module "github_org" {
  source      = "github.com/gjed/github-configs-template//terraform?ref=v1.0.0"
  config_path = "${path.root}/config"
}

Pin ref to a release tag for reproducible builds.

Consumer directory layout

your-org-configs/
├── main.tf
├── outputs.tf          # optional — pass-through of module outputs
├── backend.tf          # optional — remote state (see below)
└── config/
    ├── config.yml
    ├── group/
    │   └── base.yml
    ├── repository/
    │   └── *.yml
    ├── ruleset/
    │   └── *.yml       # optional
    └── webhook/
        └── *.yml       # optional

A ready-to-copy starting point is in examples/consumer/.

Module variables

Variable Type Required Default Description
config_path string yes Path to the config/ directory.
webhook_secrets map(string) no {} Webhook secrets for env:VAR_NAME patterns.

Important: config_path must be a static string — e.g. "${path.root}/config". Computed values (data sources, locals with unknown values) are not supported because file() and fileset() are evaluated at plan time.

Module outputs

Output Description
organization GitHub org name read from config.yml
repository_count Number of managed repositories
repositories Map of { name, url, ssh_url, visibility } per repo
subscription_tier Subscription tier from config.yml
subscription_warnings Non-null when rulesets are skipped on free tier
duplicate_key_warnings Non-null when duplicate keys appear across config files

Reference outputs in your root module:

output "repositories" {
  value = module.github_org.repositories
}

Provider configuration

The module does not configure the GitHub provider. You must declare it in your consumer root with owner set to your organization name:

provider "github" {
  owner = "your-org-name"

  # Optional: rate limiting for large organizations (100+ repos)
  # read_delay_ms  = 0
  # write_delay_ms = 100
}

The provider is configured before Terraform evaluates the module, so the org name from config.yml is not available at that stage — set it explicitly here.

Remote state

Store state remotely for team use. Copy examples/consumer/backend.tf.example to backend.tf and uncomment the relevant block:

# S3 (AWS)
terraform {
  backend "s3" {
    bucket  = "your-terraform-state-bucket"
    key     = "github-org/terraform.tfstate"
    region  = "us-east-1"
    encrypt = true
  }
}

Onboarding and offboarding scripts

The scripts/onboard-repos.sh and scripts/offboard-repos.sh scripts work with both the direct layout (this repo used as a root) and the wrapped layout.

When using the module from a consumer root, pass --module-path so the scripts target the correct Terraform state paths:

# Import a repo into state
./scripts/onboard-repos.sh \
  --module-path "module.github_org." \
  --import my-repo

# Remove a repo from state
./scripts/offboard-repos.sh \
  --module-path "module.github_org." \
  my-repo

# List repos in state
./scripts/offboard-repos.sh \
  --module-path "module.github_org." \
  --list

Migrating an existing fork

If you currently use this repo directly as a Terraform root, follow these steps:

  1. Create a consumer directory with the minimal main.tf shown above.

  2. Generate the terraform state mv commands with the migration helper:

    # Preview (safe — no changes)
    ./scripts/migrate-state.sh
    
    # Execute
    ./scripts/migrate-state.sh --execute
  3. Add a provider "github" block to your new main.tf.

  4. Run terraform init and terraform plan to verify the state migration.

Clone this wiki locally