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 GitHub Provider GitOps Workflow
What is Infrastructure 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.

IaC transforms infrastructure management from an error-prone manual process into a repeatable, auditable, and collaborative practice. It's the foundation of modern DevOps and cloud operations.
Terraform Basics: The Core Concepts

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"
}
GitHub Terraform Provider: Manage GitHub as Code

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"
}
Managing Repository Settings with Terraform

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 Management with Remote Backends

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"
  }
}
Automating Terraform with GitHub Actions

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
For pull requests, you can use the terraform plan output as a comment. Tools like terraform-docs or custom scripts can format the plan nicely for review.
GitOps: Infrastructure Changes via Pull Requests

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.

Terraform Best Practices with GitHub

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.

Using Terraform with GitHub transforms infrastructure management into a collaborative, auditable, and repeatable process. It's a cornerstone of modern DevOps practices.
Frequently Asked Questions
What's the difference between Terraform and GitHub Actions?
Terraform is an infrastructure provisioning tool that creates and manages cloud resources. GitHub Actions is a CI/CD automation platform. They work together: Actions can run Terraform commands to automate infrastructure deployments whenever code changes.
Can I manage GitHub Actions secrets with Terraform?
Yes! The GitHub provider includes the github_actions_secret resource. You can create and manage secrets across repositories, perfect for bootstrapping CI/CD configurations.
How do I handle Terraform state for large teams?
Use a remote backend with state locking. AWS S3 + DynamoDB is popular. Terraform Cloud is also excellent for team collaboration with built-in state management, run triggers, and policy controls.
Can I import existing GitHub repositories into Terraform?
Yes! Use terraform import to bring existing resources into Terraform state. You can import repositories, teams, webhooks, and more, then manage them with Terraform going forward.
What are Terraform modules and how do I use them?
Modules are reusable Terraform configurations. You can create a module for a standard microservice setup, store it in a GitHub repository, and reference it with source = "github.com/org/module//path". Modules promote DRY principles across your infrastructure.
How do I manage multiple environments (dev, staging, prod)?
Use Terraform workspaces or separate directories with different backend.tf configurations. Many teams use a folder structure like environments/dev and environments/prod with variables for environment-specific values.
What happens if someone manually changes infrastructure outside Terraform?
This is called "configuration drift." Next time you run 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.
How do I use OIDC with Terraform in GitHub Actions?
Configure your cloud provider to trust GitHub's OIDC issuer. Then in your workflow, you can use actions like aws-actions/configure-aws-credentials to obtain temporary credentials without storing any long-lived secrets. Terraform will automatically use these credentials.
Previous: GitHub Packages Next: GitOps Principles

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.