Terraform CI CD pipeline with GitHub Actions tutorial — Complete Guide
A practical, in-depth guide to Terraform CI CD pipeline with GitHub Actions tutorial with examples.
INTRO
Infrastructure as Code (IaC) promises repeatable, version‑controlled environments, but the moment you push a change to your Terraform repository you’re left with a manual, error‑prone process: terraform init, plan, apply run on a local machine, credentials scattered across shells, and no audit trail of who approved what. In a fast‑moving team this quickly becomes a bottleneck—developers wait for a teammate to run the plan, secrets leak, and drift creeps in because the same steps aren’t enforced on every PR.
A CI/CD pipeline built on GitHub Actions eliminates those pain points. By treating Terraform the same way we treat application code—run tests, enforce policies, and only apply after a gated review—you get a single source of truth for both code and infrastructure. The pipeline also gives you immutable logs, automatic rollbacks on failure, and the ability to scale approvals across multiple environments without handing out privileged keys.
If you’ve ever wrestled with “works on my machine” Terraform bugs, or spent hours writing ad‑hoc scripts to spin up a staging environment, this approach will feel like a breath of fresh air. The full guide walks you through a production‑ready workflow, from secret management in GitHub Environments to state locking with Terraform Cloud, so you can finally let the pipeline do the heavy lifting while you focus on delivering features.
WHAT YOU'LL LEARN
- How to structure a Terraform repo for multi‑environment deployments (dev, staging, prod).
- Setting up GitHub Environments and OIDC to inject AWS credentials without static secrets.
- Writing a reusable GitHub Actions workflow that runs
terraform fmt,validate,plan, andapplywith proper approvals. - Integrating Terraform Cloud for remote state, locking, and Sentinel policy checks.
- Automating PR comments that post the plan output, making reviewers’ jobs easier.
- Common pitfalls—state file leakage, plan drift, and how to avoid them with best‑practice configurations.
A SHORT CODE SNIPPET
// .github/workflows/terraform.yml
name: Terraform CI/CD
on:
pull_request:
paths:
- '**/*.tf'
push:
branches: [ main ]
jobs:
plan:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write // needed for OIDC
steps:
- uses: actions/checkout@v3
- name: Configure AWS credentials via OIDC
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Terraform Init & Plan
run: |
terraform init -backend-config="key=${{ github.ref_name }}/terraform.tfstate"
terraform plan -out=plan.out
- name: Post plan as PR comment
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const plan = fs.readFileSync('plan.out', 'utf8');
github.rest.issues.createComment({
...context.repo,
issue_number: context.payload.pull_request.number,
body: `\`\`\`terraform\n${plan}\n\`\`\``
});
The snippet shows the core idea: a single job that authenticates to AWS using OIDC, runs terraform init and plan, then posts the plan output back to the pull request. The rest of the guide expands this into separate apply jobs, environment protection rules, and state management.
KEY TAKEAWAYS
- Treat Terraform like any other codebase: lint, test, and review it in pull requests, not after merge.
- Never store static credentials: leverage GitHub OIDC and AWS IAM roles to keep secrets out of the repo.
- Remote state is non‑negotiable: Terraform Cloud or an S3 backend with DynamoDB locking prevents concurrent modifications and drift.
- Feedback loops matter: posting the plan to the PR gives reviewers immediate visibility, reducing back‑and‑forth and speeding up approvals.
👉 Read the complete guide with step-by-step examples, common mistakes, and production tips:
Terraform CI CD pipeline with GitHub Actions tutorial — Complete Guide
Top comments (0)