IaC: Terraform & GitHub
Infrastructure as Code with Terraform lets you manage your cloud resources through code. Combined with GitHub, you get version-controlled infrastructure, automated deployments, and a complete GitOps workflow. Learn to use the GitHub Terraform provider to manage repositories, teams, and webhooks as code.
Infrastructure as Code (IaC) is the practice of managing and provisioning infrastructure through machine-readable definition files, rather than manual processes or interactive configuration tools. With IaC, your servers, databases, networks, and other infrastructure components are defined in code that lives alongside your application code in version control.
Terraform is the leading IaC tool. It allows you to declare your desired infrastructure state using a simple, human-readable language called HCL (HashiCorp Configuration Language). Terraform then creates an execution plan and provisions the infrastructure across providers like AWS, Azure, Google Cloud, and even GitHub itself. This approach brings all the benefits of software development to infrastructure management: version control, code review, testing, and repeatability.
Terraform configuration files define the resources you want to create. These files use HCL syntax and typically end with .tf. The core components of a Terraform configuration are providers, resources, variables, outputs, and state.
Providers are plugins that interact with APIs. The AWS provider manages AWS resources, the Azure provider manages Azure, and the GitHub provider manages GitHub resources like repositories and teams.
Resources are the actual infrastructure components. A resource might be an AWS EC2 instance, an S3 bucket, or a GitHub repository. You define resources with a type and a name, along with configuration arguments.
State is Terraform's most important concept. After running terraform apply, Terraform stores the current state of your infrastructure in a state file. This file maps your configuration to real-world resources. For teams, state should be stored remotely—often in an S3 bucket or Terraform Cloud—so everyone works from the same state.
# Basic Terraform configuration example
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "example" {
bucket = "my-example-bucket"
acl = "private"
}
The GitHub Terraform provider allows you to manage GitHub resources using Terraform. This includes repositories, teams, members, branches, webhooks, and even GitHub Actions secrets. You can treat your GitHub organization's configuration as code, making it auditable, repeatable, and version-controlled.
With the GitHub provider, you can automate repository creation with consistent settings across your organization. Create teams with predefined permissions, add members, and configure branch protection rules—all from Terraform. This is especially valuable for organizations that need to provision new projects or onboard new team members frequently.
# GitHub provider configuration
terraform {
required_providers {
github = {
source = "integrations/github"
version = "~> 6.0"
}
}
}
provider "github" {
token = var.github_token
owner = "my-organization"
}
# Create a repository
resource "github_repository" "example" {
name = "terraform-managed-repo"
description = "Repository created and managed by Terraform"
private = true
auto_init = true
has_issues = true
has_wiki = true
}
# Create a team
resource "github_team" "developers" {
name = "developers"
description = "Development team"
privacy = "closed"
}
# Add team to repository
resource "github_team_repository" "dev_repo" {
team_id = github_team.developers.id
repository = github_repository.example.name
permission = "push"
}
The GitHub provider can manage nearly every aspect of a repository. You can configure branch protection rules, manage collaborators, set up webhooks, and even create repository environments. This ensures consistency across all repositories in your organization.
Branch protection rules are particularly important. You can enforce that all changes go through pull requests, require status checks, and restrict who can push to protected branches. By defining these in Terraform, you guarantee that every new repository gets the same security baseline.
Webhooks allow you to integrate with external systems. For example, you can automatically create webhooks for CI/CD systems, chat integrations, or monitoring tools. Terraform can manage the webhook URL, events, and secrets.
# Branch protection rule
resource "github_branch_protection" "main" {
repository_id = github_repository.example.node_id
pattern = "main"
required_status_checks {
strict = true
contexts = ["CI"]
}
required_pull_request_reviews {
required_approving_review_count = 1
dismiss_stale_reviews = true
}
restrict_pushes {
blocks_creations = true
}
}
# Repository webhook
resource "github_repository_webhook" "ci_webhook" {
repository = github_repository.example.name
configuration {
url = "https://ci.example.com/webhook"
content_type = "json"
secret = var.webhook_secret
}
active = true
events = ["push", "pull_request"]
}
State is critical for Terraform. It stores the mapping between your configuration and real-world resources. When working in a team, you must use remote state storage so everyone has access to the same state. GitHub doesn't have a built-in Terraform backend, but you can use cloud storage like AWS S3, Azure Storage, or Google Cloud Storage.
For many teams, the S3 backend is the most popular choice. You configure a bucket to store the state file, and optionally enable state locking with DynamoDB to prevent concurrent modifications. This ensures that two people don't run terraform apply at the same time and cause conflicts.
# Remote state configuration (S3)
terraform {
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "github/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-locks"
}
}
# Alternative: Azure Storage backend
terraform {
backend "azurerm" {
storage_account_name = "tfstateaccount"
container_name = "tfstate"
key = "github.terraform.tfstate"
}
}
GitHub Actions provides the perfect platform to run Terraform automatically. You can create workflows that run terraform plan on pull requests and terraform apply when changes merge to main. This gives you a complete GitOps workflow—infrastructure changes go through the same review process as code changes.
A typical workflow includes: checking out the code, setting up Terraform, initializing with the remote backend, running terraform fmt -check to validate formatting, running terraform plan to show what will change, and posting the plan as a comment on the pull request. When changes are merged to main, the workflow runs terraform apply automatically.
# GitHub Actions workflow for Terraform
name: Terraform
on:
pull_request:
branches: [ main ]
push:
branches: [ main ]
jobs:
terraform:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.7.0
- name: Terraform Init
run: terraform init
- name: Terraform Format
run: terraform fmt -check -recursive
- name: Terraform Plan
run: terraform plan -out=tfplan
- name: Apply
if: github.ref == 'refs/heads/main'
run: terraform apply tfplan
terraform plan output as a comment. Tools like terraform-docs or custom scripts can format the plan nicely for review.
GitOps is a practice where the entire infrastructure is managed through Git. All changes to infrastructure are made via pull requests, reviewed by peers, and applied automatically when merged. This brings the same benefits to infrastructure that we enjoy for application code: auditability, review, and rollback capabilities.
In a GitOps workflow with Terraform and GitHub, developers create a pull request to change Terraform configuration. The CI system runs terraform plan and posts the results. Reviewers can see exactly what resources will be created, modified, or destroyed. After approval, the PR is merged, and CI automatically runs terraform apply.
This workflow ensures that no one can make direct changes to infrastructure. Everything goes through the same review process, and the Git history provides a complete audit trail of who changed what and when. If something goes wrong, you can revert to a previous commit to roll back infrastructure changes.
Use remote state with locking. Never store state files locally in a team. Use S3 with DynamoDB locking, Terraform Cloud, or another remote backend to prevent state conflicts.
Store state separately from configuration. Keep your state in a dedicated location, often a separate repository or account. This prevents accidental deletion of state when cleaning up infrastructure.
Use modules for reusability. Create reusable Terraform modules for common patterns like a standard web application infrastructure or a microservice setup. Store modules in GitHub repositories and version them.
Manage secrets with GitHub secrets. Never hardcode tokens or keys in Terraform configuration. Use GitHub secrets and reference them as environment variables in your Actions workflows.
Validate before applying. Always run terraform plan in pull requests and review the output before applying. Use terraform fmt and terraform validate as pre-commit checks.
Tag your resources. Use consistent tagging for all resources created by Terraform. Include tags like Environment, ManagedBy, and Repository to easily identify infrastructure ownership.
github_actions_secret resource. You can create and manage secrets across repositories, perfect for bootstrapping CI/CD configurations.terraform import to bring existing resources into Terraform state. You can import repositories, teams, webhooks, and more, then manage them with Terraform going forward.source = "github.com/org/module//path". Modules promote DRY principles across your infrastructure.backend.tf configurations. Many teams use a folder structure like environments/dev and environments/prod with variables for environment-specific values.terraform plan, it will show the discrepancy. You can use terraform refresh to update state, then decide whether to revert the manual change or update the configuration to match.aws-actions/configure-aws-credentials to obtain temporary credentials without storing any long-lived secrets. Terraform will automatically use these credentials.Terraform and GitHub together create the perfect platform for Infrastructure as Code. Version your infrastructure, review changes, and automate deployments—all with the tools you already know.