Manage GitHub repositories, teams, and settings as code using Terraform with Lynx backend for collaborative state management.
- Overview
- Features
- Prerequisites
- Quick Start
- Repository Structure
- Usage
- Examples
- Modules
- Best Practices
- Contributing
This repository uses the GitHub Terraform Provider to manage GitHub resources declaratively. Perfect for organizations that want to:
- Standardize repository configurations
- Enforce security policies across all repos
- Automate repository creation and setup
- Manage team access and permissions at scale
- Version control your GitHub organization settings
- Repository Management: Create, configure, and manage GitHub repositories
- Branch Protection: Enforce branch protection rules and required reviews
- Team Management: Manage teams and repository access levels
- Secret Management: Provision repository and organization secrets
- Webhook Configuration: Automate webhook setup for CI/CD
- Issue Labels: Standardize labels across repositories
- Actions Settings: Configure GitHub Actions permissions and runners
- Organization Settings: Manage organization-wide policies
- Lynx State Backend: Collaborative state management with versioning and rollback
- Terraform >= 1.5.0
- Lynx Backend - For state management
- GitHub account with appropriate permissions:
- Organization Owner (for org-level resources)
- Admin access (for repository management)
- GitHub Personal Access Token (PAT) or GitHub App
- Lynx API credentials (API URL and API Key)
For Classic PAT, enable these scopes:
repo(Full control of private repositories)admin:org(Full control of orgs and teams)delete_repo(Delete repositories)admin:repo_hook(Full control of repository hooks)workflow(Update GitHub Action workflows)
For Fine-grained PAT, grant:
- Repository permissions: Administration (Read/Write)
- Organization permissions: Administration (Read/Write), Members (Read/Write)
git clone https://github.com/yourusername/terraform-github-management.git
cd terraform-github-management# GitHub authentication
export GITHUB_TOKEN="ghp_your_personal_access_token"
# Or use GitHub App authentication
export GITHUB_APP_ID="your_app_id"
export GITHUB_APP_INSTALLATION_ID="your_installation_id"
export GITHUB_APP_PEM_FILE="path/to/app-private-key.pem"
# Lynx backend credentials
export LYNX_API_URL="https://lynx.company.com/api/v1"
export LYNX_API_KEY="your-lynx-api-key"Create or update backend.tf:
terraform {
backend "http" {
address = "https://lynx.company.com/api/v1/terraform/state/github-management/production"
lock_address = "https://lynx.company.com/api/v1/terraform/lock/github-management/production"
unlock_address = "https://lynx.company.com/api/v1/terraform/lock/github-management/production"
lock_method = "POST"
unlock_method = "DELETE"
# Authentication via headers
username = "api-key"
password = var.lynx_api_key
}
}Create terraform.tfvars:
github_organization = "your-org-name"
github_owner = "your-username"
default_branch_protection = {
require_code_owner_reviews = true
required_approving_review_count = 2
dismiss_stale_reviews = true
require_conversation_resolution = true
}terraform init
terraform plan
terraform apply.
βββ modules/
β βββ repository/
β β βββ main.tf # Repository resource
β β βββ variables.tf
β β βββ outputs.tf
β β βββ README.md
β βββ team/
β β βββ main.tf # Team management
β β βββ variables.tf
β β βββ outputs.tf
β βββ branch-protection/
β β βββ ...
β βββ webhooks/
β βββ ...
βββ environments/
β βββ organization/
β β βββ main.tf # Org-wide settings
β β βββ repositories.tf # All repositories
β β βββ teams.tf # Team definitions
β β βββ terraform.tfvars
β βββ personal/
β βββ ...
βββ templates/
β βββ repository/
β β βββ standard-repo.tf # Template for new repos
β β βββ microservice.tf # Microservice template
β βββ policies/
β βββ branch-protection.tf
βββ scripts/
β βββ generate-repo.sh # Helper script
β βββ bulk-import.sh # Import existing repos
βββ .gitignore
βββ backend.tf # Lynx backend configuration
βββ providers.tf
βββ variables.tf
βββ outputs.tf
βββ README.md
terraform {
required_version = ">= 1.5.0"
required_providers {
github = {
source = "integrations/github"
version = "~> 6.0"
}
}
}
provider "github" {
token = var.github_token
owner = var.github_organization
}terraform {
backend "http" {
address = "https://lynx.company.com/api/v1/terraform/state/platform/github-management/production"
lock_address = "https://lynx.company.com/api/v1/terraform/lock/platform/github-management/production"
unlock_address = "https://lynx.company.com/api/v1/terraform/lock/platform/github-management/production"
lock_method = "POST"
unlock_method = "DELETE"
username = "api-key"
password = var.lynx_api_key
}
}variable "github_token" {
description = "GitHub Personal Access Token"
type = string
sensitive = true
}
variable "lynx_api_key" {
description = "Lynx API Key for backend authentication"
type = string
sensitive = true
}
variable "github_organization" {
description = "GitHub organization name"
type = string
}# Copy to terraform.tfvars and fill in your values
github_organization = "your-org-name"
# Set via environment variables:
# export TF_VAR_github_token="ghp_..."
# export TF_VAR_lynx_api_key="..."module "new_repository" {
source = "./modules/repository"
name = "my-new-repo"
description = "Repository description"
visibility = "private"
has_issues = true
has_projects = true
has_wiki = false
has_downloads = true
auto_init = true
gitignore_template = "Python"
license_template = "mit"
topics = ["python", "api", "microservice"]
# Team access
teams = {
"developers" = "push"
"admins" = "admin"
}
}module "branch_protection" {
source = "./modules/branch-protection"
repository = "my-repo"
branch = "main"
require_code_owner_reviews = true
required_approving_review_count = 2
dismiss_stale_reviews = true
require_conversation_resolution = true
require_signed_commits = true
required_status_checks = {
strict = true
contexts = [
"ci/test",
"ci/lint",
"security/scan"
]
}
restrictions = {
users = ["admin-user"]
teams = ["platform-team"]
}
}module "engineering_team" {
source = "./modules/team"
name = "engineering"
description = "Engineering team"
privacy = "closed"
members = {
"user1" = "maintainer"
"user2" = "member"
"user3" = "member"
}
repositories = {
"backend-api" = "push"
"frontend-app" = "push"
"infrastructure" = "pull"
}
}resource "github_actions_secret" "api_key" {
repository = "my-repo"
secret_name = "API_KEY"
plaintext_value = var.api_key
}
resource "github_actions_organization_secret" "shared_token" {
secret_name = "SHARED_TOKEN"
visibility = "all"
plaintext_value = var.shared_token
}module "api_service" {
source = "./modules/repository"
name = "payment-api"
description = "Payment processing API service"
visibility = "private"
# Repository settings
has_issues = true
has_projects = false
has_wiki = false
auto_init = true
# Templates
gitignore_template = "Node"
license_template = "apache-2.0"
# Topics for discoverability
topics = ["nodejs", "api", "payments", "microservice"]
# Default branch
default_branch = "main"
# Enable vulnerability alerts
vulnerability_alerts = true
# Team permissions
teams = {
"backend-team" = "push"
"platform-team" = "admin"
"security-team" = "pull"
}
}
# Branch protection for main
resource "github_branch_protection" "api_main" {
repository_id = module.api_service.repository_id
pattern = "main"
required_pull_request_reviews {
dismiss_stale_reviews = true
require_code_owner_reviews = true
required_approving_review_count = 2
require_last_push_approval = true
}
required_status_checks {
strict = true
contexts = [
"ci/build",
"ci/test",
"ci/lint",
"security/sast"
]
}
enforce_admins = true
require_signed_commits = true
require_conversation_resolution = true
push_restrictions = [
"/platform-team"
]
}# Default labels for all repositories
resource "github_issue_label" "bug" {
repository = each.value
for_each = toset(var.all_repositories)
name = "bug"
color = "d73a4a"
description = "Something isn't working"
}
# Organization settings
resource "github_organization_settings" "main" {
billing_email = "billing@company.com"
company = "Your Company"
blog = "https://blog.company.com"
email = "github@company.com"
has_organization_projects = true
has_repository_projects = true
default_repository_permission = "read"
members_can_create_repositories = false
members_can_create_public_repositories = false
members_can_create_private_repositories = false
web_commit_signoff_required = true
}
# Organization security settings
resource "github_organization_security_manager" "security_team" {
team_slug = "security"
}resource "github_repository_webhook" "ci_webhook" {
repository = "my-repo"
configuration {
url = "https://ci.company.com/webhook"
content_type = "json"
insecure_ssl = false
secret = var.webhook_secret
}
active = true
events = [
"push",
"pull_request",
"release"
]
}Creates and configures a GitHub repository with all settings.
Inputs:
name- Repository namedescription- Repository descriptionvisibility- public/private/internalhas_issues- Enable issueshas_wiki- Enable wikiteams- Team access map
Outputs:
repository_id- Repository IDfull_name- Full repository namehtml_url- Repository URLssh_clone_url- SSH clone URL
Manages GitHub teams and their repository permissions.
Inputs:
name- Team namedescription- Team descriptionprivacy- secret/closedmembers- Map of members and rolesrepositories- Map of repos and permissions
Outputs:
team_id- Team IDteam_slug- Team slugmembers_count- Number of members
Configures branch protection rules.
Inputs:
repository- Repository namebranch- Branch patternrequire_code_owner_reviews- Require CODEOWNERS reviewrequired_approving_review_count- Number of required reviewsrequired_status_checks- Status checks that must pass
Outputs:
protection_id- Protection rule ID
Use consistent naming conventions:
<team>-<project>-<type>
backend-api-service
frontend-web-app
infrastructure-terraform
Always protect your main branch:
- β Require pull request reviews (minimum 2)
- β Require status checks to pass
- β Require conversation resolution
- β Require signed commits
- β Include administrators in restrictions
Organize teams by function:
platform-team- Infrastructure and platformbackend-team- Backend servicesfrontend-team- Frontend applicationssecurity-team- Security reviewsdata-team- Data engineering
- β Use organization secrets for shared values
- β Use repository secrets for service-specific values
- β Never commit secrets to Terraform files
- β Use Terraform variables marked as sensitive
- β Store secrets in secure vault (HashiCorp Vault, AWS Secrets Manager)
Use Lynx backend for collaborative state management:
terraform {
backend "http" {
address = "https://lynx.company.com/api/v1/terraform/state/platform/github-management/production"
lock_address = "https://lynx.company.com/api/v1/terraform/lock/platform/github-management/production"
unlock_address = "https://lynx.company.com/api/v1/terraform/lock/platform/github-management/production"
lock_method = "POST"
unlock_method = "DELETE"
username = "api-key"
password = var.lynx_api_key
}
}Benefits of Lynx:
- Team collaboration with RBAC
- Built-in state locking
- State versioning and rollback
- Web-based dashboard
- Snapshot support
Enable security features for all repositories:
vulnerability_alerts = true
security_and_analysis {
secret_scanning {
status = "enabled"
}
secret_scanning_push_protection {
status = "enabled"
}
}To import existing GitHub repositories into Terraform:
# Import repository
terraform import module.existing_repo.github_repository.main your-org/repo-name
# Import team
terraform import github_team.developers 1234567
# Import branch protection
terraform import github_branch_protection.main repo-name:mainOr use the bulk import script:
./scripts/bulk-import.sh your-orglocals {
microservices = [
"user-service",
"payment-service",
"notification-service",
"analytics-service"
]
}
module "microservice_repos" {
source = "./modules/repository"
for_each = toset(local.microservices)
name = each.value
description = "${each.value} microservice"
visibility = "private"
topics = ["microservice", "api", "nodejs"]
# Standard configuration
has_issues = true
has_wiki = false
auto_init = true
license_template = "mit"
teams = {
"backend-team" = "push"
"platform-team" = "admin"
}
}locals {
standard_labels = {
bug = {
color = "d73a4a"
description = "Something isn't working"
}
enhancement = {
color = "a2eeef"
description = "New feature or request"
}
documentation = {
color = "0075ca"
description = "Improvements or additions to documentation"
}
security = {
color = "ee0701"
description = "Security vulnerability or issue"
}
}
}
resource "github_issue_label" "standard" {
for_each = {
for pair in setproduct(var.all_repositories, keys(local.standard_labels)) :
"${pair[0]}-${pair[1]}" => {
repo = pair[0]
label = pair[1]
}
}
repository = each.value.repo
name = each.value.label
color = local.standard_labels[each.value.label].color
description = local.standard_labels[each.value.label].description
}After applying Terraform, you can view outputs:
# List all repositories
terraform output repositories
# Get specific repository URL
terraform output -json | jq '.repositories.value["my-repo"].html_url'
# List all teams
terraform output teams- Fork the repository
- Create a feature branch (
git checkout -b feature/new-template) - Make your changes
- Test with
terraform plan - Commit your changes (
git commit -am 'feat: add new repository template') - Push to the branch (
git push origin feature/new-template) - Open a Pull Request
- Never commit tokens to version control
- Use environment variables for authentication (GitHub and Lynx)
- Enable branch protection on this repository
- Use Lynx encrypted state backend with access controls
- Enable secret scanning on all repositories
- Implement CODEOWNERS file for review requirements
- Regular audit of team permissions
- Lynx RBAC: Control who can access and modify state
- GitHub Terraform Provider Documentation
- GitHub REST API Documentation
- GitHub Security Best Practices
This project is licensed under the MIT License - see the LICENSE file for details.
- Platform Team - @platform-team
- Contact - platform@company.com
See CHANGELOG.md for version history.
Manage GitHub at scale with Infrastructure as Code
Made with β€οΈ by the Platform Team