Terraform Pull Request Automation: A Complete Guide

Comprehensive guide to implementing pull request-based Terraform workflows, from basic automation to enterprise-scale governance.

Why PR-Based Workflows for Infrastructure as Code

Organizations implementing cross-team Terraform PR automation report 40-70% reductions in infrastructure provisioning time. Deutsche Bank's platform team now enables hundreds of development teams to provision compliant infrastructure within minutes, while Spotify successfully migrated 1,200+ microservices using automated Terraform workflows.

The shift from manual infrastructure management to automated workflows has become essential. Teams face a critical decision: choosing between open-source solutions requiring significant maintenance effort or commercial platforms offering managed services with advanced features.

PR-based workflows provide several key advantages:

  • Visibility: All infrastructure changes are visible in pull requests before deployment
  • Collaboration: Teams discuss changes in a familiar VCS context
  • Auditability: Complete history of who changed what and why
  • Safety: Automated checks prevent problematic changes from reaching production
  • Parallelism: Multiple teams can work simultaneously without conflicts

Understanding PR Workflow Architecture

Terraform pull request automation typically follows one of two patterns: merge-before-apply or apply-before-merge.

Merge-Before-Apply (Apply After Merge)

This is the most recommended and safest approach:

  1. Developers create feature branches and submit PRs
  2. Automated checks and terraform plan runs
  3. Peers review the plan
  4. Only after PR approval and merge into main, terraform apply executes via CI/CD
  5. This keeps main as the source of truth and changes linear

Benefits:

  • Low risk of state corruption
  • High main branch integrity
  • Simple mental model

Trade-offs:

  • Slower feedback loop (apply happens post-merge)
  • Issues discovered after merge require another fix PR

Apply-Before-Merge (Plan-on-PR Pattern)

Some teams apply before merging to verify changes in a real environment. This approach critically depends on sophisticated tooling like Atlantis or Scalr.

Key requirements:

  • PR-level locking to prevent concurrent applies
  • Automated plan/apply commands via PR comments
  • Robust state management with remote backends

Benefits:

  • Faster feedback (detect issues before merge)
  • Test changes in real environment

Trade-offs:

  • Higher complexity
  • Requires powerful tooling to manage safely

Plan-on-PR Patterns: Best Practices

Every pull request should trigger an automatic terraform plan to show reviewers what changes are coming. This visibility is non-negotiable.

GitHub Actions Implementation

Here's a production-ready workflow incorporating best practices:

name: Terraform PR Automation
on:
  pull_request:
    paths:
      - 'terraform/**'
      - '.github/workflows/terraform.yml'

permissions:
  id-token: write
  contents: read
  pull-requests: write

jobs:
  terraform-check:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        environment: [dev, staging, prod]

    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions
          aws-region: us-east-1

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.6.0
          terraform_wrapper: false

      - name: Terraform Format Check
        run: terraform fmt -check -recursive

      - name: Terraform Init
        working-directory: terraform/${{ matrix.environment }}
        run: |
          terraform init \
            -backend-config="bucket=${{ secrets.TF_STATE_BUCKET }}" \
            -backend-config="key=${{ matrix.environment }}/terraform.tfstate"

      - name: Terraform Validate
        working-directory: terraform/${{ matrix.environment }}
        run: terraform validate

      - name: Run Security Scan
        uses: aquasecurity/tfsec-pr-commenter-action@v1
        with:
          working_directory: terraform/${{ matrix.environment }}
          github_token: ${{ github.token }}

      - name: Terraform Plan
        id: plan
        working-directory: terraform/${{ matrix.environment }}
        run: |
          terraform plan -out=tfplan -no-color 2>&1 | tee plan_output.txt
          echo "exitcode=$?" >> $GITHUB_OUTPUT

GitLab CI Implementation

GitLab's native Terraform integration simplifies state management:

stages:
  - validate
  - plan

variables:
  TF_ROOT: ${CI_PROJECT_DIR}/terraform
  TF_STATE_NAME: ${CI_ENVIRONMENT_NAME}

.terraform-base:
  image: hashicorp/terraform:1.6
  before_script:
    - cd ${TF_ROOT}/${CI_ENVIRONMENT_NAME}
    - terraform init

validate:
  extends: .terraform-base
  stage: validate
  script:
    - terraform fmt -check -recursive
    - terraform validate
  rules:
    - if: $CI_MERGE_REQUEST_ID

plan:dev:
  extends: .terraform-base
  stage: plan
  environment:
    name: development
  script:
    - terraform plan -out=tfplan
    - terraform show -json tfplan > plan.json
  artifacts:
    paths:
      - ${TF_ROOT}/${CI_ENVIRONMENT_NAME}/tfplan
      - ${TF_ROOT}/${CI_ENVIRONMENT_NAME}/plan.json
    reports:
      terraform: ${TF_ROOT}/${CI_ENVIRONMENT_NAME}/plan.json
  rules:
    - if: $CI_MERGE_REQUEST_ID

Apply Strategies: Merge-Before-Apply vs Apply-Before-Merge

Merge-Before-Apply (Manual CI/CD)

name: 'Terraform Apply on Merge'
on:
  push:
    branches: [ main ]
jobs:
  apply:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - uses: hashicorp/setup-terraform@v3
    - run: terraform init
      working-directory: ./terraform
    - run: terraform plan -input=false -out=tfplan
      working-directory: ./terraform
    - run: terraform apply -auto-approve tfplan
      working-directory: ./terraform

When to use:

  • Small teams (1-10 developers)
  • Risk-averse organizations
  • Strict change control requirements

Apply-Before-Merge (Specialized Tooling Required)

Platforms like Scalr (or tools like Atlantis) manage apply triggers based on workspace settings and PR interactions:

  1. On PR creation, a plan is generated and posted
  2. After approvals, workflows diverge:
    • Apply After Merge (Scalr default): Merge triggers platform apply from target branch
    • Apply Before Merge (common in Atlantis): PR comment (e.g., /atlantis apply/scalr apply) triggers controlled apply with platform managing locks and state

When to use:

  • Medium to large teams (50+ developers)
  • Need for fast feedback loops
  • Mature infrastructure practices

Atlantis: PR Automation Fundamentals

Atlantis revolutionized Terraform PR automation by bringing automation directly into pull requests.

How Atlantis Works

  1. Open a PR with your Terraform changes
  2. Comment atlantis plan
  3. Atlantis runs the plan, dumps the output back in the PR
  4. Team reviews; looks good?
  5. Comment atlantis apply; Atlantis runs the apply, done

Configuration Example

version: 3
projects:
- name: production
  dir: environments/prod
  terraform_version: v1.5.0
  autoplan:
    when_modified: ["*.tf", "*.tfvars"]
    enabled: true
  apply_requirements: ["approved", "mergeable"]

- name: staging
  dir: environments/staging
  terraform_version: v1.5.0
  autoplan:
    when_modified: ["*.tf", "*.tfvars"]
    enabled: true

Strengths of Atlantis

  • Developer-friendly: Git workflows teams already know
  • Quick feedback: See the plan right away
  • Collaboration: All discussion happens in one place
  • Transparency: Everyone sees what's happening
  • Open source: Full control, no licensing costs

Limitations at Scale

  • Operational overhead: Self-hosted, requires maintenance and security management
  • Governance gaps: OPA policies require custom integrations
  • Scaling challenges: Single server becomes a bottleneck with many repos
  • State management: Getting locking right across many operations needs careful setup
  • Audit complexity: Compliance features may need additional tooling

Scalr: Enterprise PR Automation

Scalr takes the Atlantis-style PR workflow and wraps it in a complete enterprise platform.

VCS-Driven Workflows

Link a workspace to a repo branch, and Scalr gets notified of PRs and merges. It supports two main GitOps flows:

  1. Merge-Before-Apply: Plan on PR, review, merge, then Scalr applies from main (classic)
  2. Apply-Before-Merge: Plan and apply right from the PR before it hits main (Atlantis-style)

PR Comment Commands

# See the plan
/scalr plan

# Approve a waiting run
/scalr approve -workspace-id=ws-xxxxxxxxxx

# Apply changes (requires permission)
/scalr apply

# Target specific workspace
/scalr apply -workspace-id=ws-xxxxxxxxxx

Feedback comes right back into the PR with smart summaries for clean plans and detailed reports for errors.

Safety Features

Branch-aware protection:

  • Warns if trying to change state from unmerged PR branch
  • Prevents auto-applies if branch differs from workspace's main configuration
  • Ensures state changes originate from main or verified PR workflows

Controlled applies:

  • Requires PR approval and branch protection checks
  • Skip post-merge apply with [skip scalr] or [skip ci] in merge message
  • RBAC controls: only users with runs:apply permission can execute

Custom Hooks for Workflows

Scalr lets you inject scripts at different run phases: pre-init, pre-plan, post-plan, pre-apply, post-apply.

Example pre-plan hook to run tflint:

#!/bin/bash
echo "--- Running TFLint ---"

if ! command -v tflint &> /dev/null; then
    mkdir -p /tmp/bin
    curl -s https://raw.githubusercontent.com/terraform-linters/tflint/master/install_linux.sh | TFLINT_INSTALL_PATH=/tmp/bin bash
    export PATH="$PATH:/tmp/bin"
fi

if [ -f ".tflint.hcl" ]; then
    tflint --config=.tflint.hcl --recursive .
else
    tflint --recursive .
fi

OPA Policy Integration

Write policies in Rego, store in Git, and Scalr checks them pre-plan and post-plan:

package terraform

import input.tfrun as tfrun

# Deny if estimated monthly cost delta exceeds $100
deny[reason] {
    cost_delta := tfrun.cost_estimate.delta_monthly_cost
    max_allowed_cost_delta := 100.00

    cost_delta > max_allowed_cost_delta
    reason := sprintf("Cost increase is $%.2f. We allow up to $%.2f.",
        [cost_delta, max_allowed_cost_delta])
}

Policies can be advisory (warning only), soft-mandatory (requires approval), or hard-mandatory (blocks run).

Hierarchical Organization Model

Account (top level)
├── Environment (groups of workspaces)
│   └── Workspace (where Terraform runs)

Standards set at Account/Environment level flow down to all workspaces, enabling consistent governance across teams.


Preventing State Updates from Unmerged PRs

A critical challenge in Terraform PR automation is preventing state corruption from changes applied before PR merge.

The Risks of Pre-Merge Applies

State Corruption and Conflicts: Multiple developers applying from unmerged PRs to shared state causes race conditions, overwrites, and inconsistent state.

Infrastructure Drift: The main branch becomes the single source of truth. Applying from unmerged PRs creates divergence where live infrastructure doesn't match main's definition.

Compromised Main Branch Integrity: Hidden issues in pre-applied changes, provider errors, or API limits can cause actual infrastructure to differ from intended state.

Traceability Challenges: Audit trails become obscured, and rollbacks become complex when changes weren't merged into main first.

Mitigation Strategies

"Apply After Merge" Gold Standard: Ensures main is source of truth and changes remain linear.

"Apply Before Merge" with Sophisticated Tooling: Requires PR-level locking, automated plan/apply via comments, and remote state backends with locking.

Essential Supporting Practices:

  • Remote state backends with locking (S3 + DynamoDB, GCS, Azure Blob)
  • Automated terraform plan in all PRs
  • Platform-level enforcement of branch awareness

Platform Enforcement

Modern platforms like Scalr provide built-in safeguards:

  • Branch awareness: Understands implications of code branches for your state
  • Risk warnings: Flags risky operations and unmerged PR changes
  • Auto-apply prevention: Stops auto-applies if state branch differs from workspace's main configuration
  • Controlled state modifications: Ensures changes originate from main or verified PR workflows

PR Review Best Practices

Plan Review Checklist

  • No unexpected resource destruction: Verify - and ~ changes are intentional
  • Cost implications: Check for expensive instance types or resource scaling
  • Security: Look for public access, open security groups, unencrypted storage
  • Naming conventions: Verify resources follow team standards
  • Tagging: Ensure all resources have required tags
  • State locking: Confirm remote backend is properly configured

Approval Workflows

# GitHub branch protection rules
Required reviewers: 2
Require status checks to pass before merging:
  - Terraform Plan (all environments)
  - Security Scan (tfsec, checkov)
  - OPA Policy Checks

Common Issues to Watch

  • Implicit provider credentials: Use IAM roles instead
  • Hard-coded values: Extract to variables
  • Overly broad permissions: Apply principle of least privilege
  • Missing dependencies: Verify module versions and providers
  • State file exposure: Ensure backend is encrypted and access-controlled

Tool Comparison Matrix

FeatureAtlantisSpaceliftTerraform CloudScalr
Apply-Before-Merge (Native)Yes (PR Comments)Yes (Proposed Runs)LimitedYes (PR Comments)
Merge-Before-ApplyNoYes (Default)Yes (Primary)Yes (Default)
Workflow Customization (Hooks)LimitedExtensiveLimitedExtensive (5 hooks)
OPA Policy DepthManual IntegrationDeep OPASentinel (Proprietary)Deep OPA
State Backend ChoiceUser's ChoiceManaged or User'sTFC Managed OnlyScalr or User's
PR Comment QualityBasic Plan OutputDetailed, CustomizableBasic Status ChecksRich & Contextual
Multi-IaC SupportTerraformMultiple ToolsTerraform, OpenTofuTerraform, OpenTofu, Terragrunt
Self-Hosted ExecutionYes (Default)Yes (Worker Pools)Yes (TFE Agents)Yes (Custom Images)
RBAC GranularityAuth-dependentGranularLimited System Roles120+ Permissions

Best Practices for 2026

Foundation Requirements

  1. Remote State with Encryption: Use S3 + DynamoDB, GCS, or Azure Blob with encryption enabled
  2. Automatic Plan on PR: Every PR must trigger terraform plan automatically
  3. Branch Protection: Require status checks and reviews before merge
  4. Audit Logging: Track who changed what and when

Scale Considerations

Small Teams (1-10):

  • GitHub Actions + S3 backend
  • Manual coordination acceptable
  • Focus on state locking

Growing Teams (10-50):

  • Atlantis for PR automation
  • Basic OPA policies
  • Shared module library
  • ~$500-1000/month operational cost

Enterprise (50-200+):

  • Enterprise platform (Scalr, Spacelift, env0)
  • Deep OPA governance
  • Hierarchical organization
  • Self-service platform for teams
  • $1500+/month for platform + engineering time saved

Workflow Maturity Progression

Stage 1 - Manual: Local Terraform runs, shared credentials, manual state locking Stage 2 - Basic Automation: CI/CD pipeline, GitHub Actions, remote state Stage 3 - PR-Driven: Atlantis-style automation, plan-on-PR, manual policy checks Stage 4 - Enterprise Governance: OPA policies, RBAC, cost management, compliance Stage 5 - Self-Service Platform: Module marketplace, policy as code, team autonomy

Avoiding Common Pitfalls

  • Don't apply from local machines in production: All production applies must go through the pipeline
  • Don't bypass branch protection: Every change must follow the same review process
  • Don't mix manual and automated state changes: One source of truth only
  • Don't ignore state conflicts: Implement robust locking and monitoring
  • Don't assume plan correctness: Always verify plan output in PR comments

Summary

Terraform pull request automation has evolved from experimental practice to enterprise necessity. The journey typically starts with basic CI/CD checks, progresses to Atlantis-style PR-based automation, and matures into governed platforms like Scalr for organizations at scale.

Key takeaways:

  • Start with merge-before-apply unless you have specific needs for pre-merge validation
  • Invest in automated plan visibility - it's the foundation of safe infrastructure changes
  • Implement branch protection - make it impossible to bypass reviews and checks
  • Choose tooling based on scale - Atlantis suits 10-50 person teams; enterprises need platforms
  • Focus on governance from day one - OPA policies, RBAC, and audit trails become non-negotiable as teams grow

The right platform choice depends on your organization's size, toolchain, and governance needs. Starting simple with GitHub Actions and scaling to specialized platforms as requirements grow is the pragmatic approach that has worked for hundreds of organizations.