Lesson 13 of 15 4 minAdvanced Track

GitHub Actions Pipeline: Automated Plan & Apply Workflows

Build an automated, OIDC-secured CI/CD pipeline using GitHub Actions to lint, test, plan, and apply infrastructure modifications.

Reading Mode

Hide the curriculum rail and keep the lesson centered for focused reading.

Key Takeaways

  • CI pipelines should validate syntax (fmt, validate) and run security audits (tfsec) on every PR.
  • Post execution plans directly back to Pull Request threads to facilitate visual team review.
  • Apply infrastructure changes only after merges to the main branch, gated by manual organization approvals.
Recommended Prerequisites
terraform-aws-12-load-balancers-autoscaling

Premium outcome

Provision, secure, and automate production-grade cloud infrastructure at scale.

Backend and platform engineers who want to design, deploy, and automate robust production environments on AWS.

You leave with

  • A secure, modular, multi-environment AWS landing zone designed from scratch
  • A fully integrated GitOps deployment pipeline using GitHub Actions and Terraform S3 Backend
  • Hands-on expertise deploying containerized microservices (ECS Fargate + RDS) with secure IAM gating

GitHub Actions Pipeline: Automated Plan & Apply Workflows

In a collaborative engineering organization, running terraform apply directly from a developer's local laptop is an anti-pattern. If an engineer applies changes locally, there is no centralized log of the deployment, no automated validation of HCL syntax, and no guarantee that the local environment matches the remote master branch.

To achieve operational consistency, we must automate our infrastructure delivery using a GitOps CI/CD pipeline via GitHub Actions.

[ Developer PR ] ──> [ GitHub Actions ] ──> Runs: fmt, validate, security, plan
                            │
                       Comment plan in PR for review
                            ▼
[ Merge to Main ] ──> [ Manual Gate ] ── Approved? ──> [ 'terraform apply' ]

Key Stages of a Production GitOps Pipeline

  1. Linting and Validation: Catch syntax errors (terraform validate) and formatting drift (terraform fmt -check) before allocating computing resources.
  2. Security Audits: Run tools like tfsec or trivy to detect common security misconfigurations (e.g. open ports, unencrypted S3 buckets).
  3. Pull Request Plans: Automatically calculate the infrastructure changes and post the visual diff directly inside the Pull Request conversation.
  4. Gated Continuous Deployment: Apply the changes only when code is merged to the main branch, utilizing GitHub Environments to enforce manual peer-approval gates.

Hands-on: Building the GitHub Actions Workflow

Create a file .github/workflows/terraform.yml in your repository:

# .github/workflows/terraform.yml

name: "Terraform GitOps Pipeline"

on:
  pull_request:
    branches: [ main ]
    paths:
      - 'infrastructure/**'
  push:
    branches: [ main ]
    paths:
      - 'infrastructure/**'

permissions:
  id-token: write # Required to fetch temporary AWS credentials via OIDC
  contents: read  # Required to checkout code
  pull-requests: write # Required to write plan comments back to PR threads

jobs:
  validate-and-plan:
    name: "1. Lint, Validate & Plan"
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_TERRAFORM_ROLE_ARN }} # temporary passwordless credentials
          aws-region: us-east-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.7.0"

      - name: Format Check
        run: terraform fmt -check
        working-directory: infrastructure/environments/prod

      - name: Initialize Workspace
        run: terraform init
        working-directory: infrastructure/environments/prod

      - name: Validate Syntax
        run: terraform validate
        working-directory: infrastructure/environments/prod

      - name: Security Scan (tfsec)
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          working_directory: infrastructure/environments/prod

      - name: Calculate Infrastructure Plan
        id: plan
        run: terraform plan -no-color -out=tfplan
        working-directory: infrastructure/environments/prod

      # Inject plan logs back into the PR conversation thread
      - name: Post Plan Comments to PR
        uses: actions/github-script@v7
        with:
          script: |
            const plan = `${{ steps.plan.outputs.stdout }}`;
            const body = `#### 🤖 Terraform Execution Plan Result:
            \`\`\`hcl
            ${plan.substring(0, 60000)}
            \`\`\``;
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: body
            })

  deploy-production:
    name: "2. Deploy to Production"
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    environment: production # Enforces manual organization approval gate
    
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_TERRAFORM_ROLE_ARN }}
          aws-region: us-east-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: "1.7.0"

      - name: Initialize Workspace
        run: terraform init
        working-directory: infrastructure/environments/prod

      - name: Execute Changes (Apply)
        run: terraform apply -auto-approve
        working-directory: infrastructure/environments/prod

Enforcing the Safety Gates

  1. GitHub Environment: Inside your GitHub repository settings, create an environment named production. Check Required Reviewers and add your team leads.
  2. Branch Protection Rules: Enforce that the main branch requires at least one approving review, and all status checks (1. Lint, Validate & Plan) must pass successfully before code can be merged.

By locking down direct production write access and funneling all deployments through an audited, multi-stage GitOps pipeline, you ensure absolute architectural stability and eliminate operational errors across your cloud environments.

Next Steps

Your pipeline is operational. However, as developers operate on AWS, changes will inevitably occur outside our pipeline—either through manual console fixes during outages or external automation. This is called Infrastructure Drift.

In the next lesson, we will explore how to detect drift automatically, run surgical State Surgery using CLI tools, and import untracked resources safely.

Want to track your progress?

Sign in to save your progress, track completed lessons, and pick up where you left off.