Skip to content

Quickstart

This guide creates a pull-request driven Terraform workflow with one non-production environment and one production environment.

The workflow uses Branch Deploy for pull request commands, permissions, checks, deployments, and locks. Terraform Branch Deploy runs Terraform after Branch Deploy accepts a command.

Prerequisites

  • A GitHub repository with Terraform code.
  • GitHub Actions enabled.
  • Cloud credentials that can be configured from a GitHub Actions job, preferably through OIDC.
  • Terraform state stored in a remote backend.

1. Add Configuration

Create .tf-branch-deploy.yml at the repository root:

.tf-branch-deploy.yml
default-environment: dev
production-environments: [prod]

environments:
  dev:
    working-directory: terraform/dev
  prod:
    working-directory: terraform/prod

The environment in the PR comment must match one of the keys under environments.

2. Add the Workflow

Create .github/workflows/deploy.yml:

Why the action appears twice

Trigger mode runs Branch Deploy first and decides whether the comment may continue. Execute mode runs Terraform only after the workflow has checked out the requested ref and configured cloud credentials.

.github/workflows/deploy.yml
name: Terraform Deploy

on:
  issue_comment:
    types: [created]

permissions:
  contents: write
  pull-requests: write
  deployments: write
  checks: read
  statuses: read
  id-token: write

jobs:
  deploy:
    if: github.event.issue.pull_request
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v6

      - uses: scarowar/terraform-branch-deploy@v0
        with:
          mode: trigger
          github-token: ${{ secrets.GITHUB_TOKEN }}
          disable-naked-commands: true
          checks: all
          outdated-mode: strict

      - uses: actions/checkout@v6
        if: env.TF_BD_CONTINUE == 'true'
        with:
          ref: ${{ env.TF_BD_REF }}

      # Add cloud credentials here.

      - uses: scarowar/terraform-branch-deploy@v0
        if: env.TF_BD_CONTINUE == 'true'
        with:
          mode: execute
          github-token: ${{ secrets.GITHUB_TOKEN }}

Trigger mode runs first. It calls Branch Deploy, checks whether the comment is allowed to proceed, and exports TF_BD_* variables for the rest of the job. Terraform does not run until execute mode.

3. Add Cloud Credentials

Put credential setup after the second checkout and before execute mode. Gate credential steps with TF_BD_CONTINUE so credentials are configured only after Branch Deploy has accepted the command.

The workflow above includes id-token: write for OIDC-based cloud authentication. Remove it only if your credential step does not use OIDC.

- uses: aws-actions/configure-aws-credentials@v5
  if: env.TF_BD_CONTINUE == 'true'
  with:
    role-to-assume: arn:aws:iam::123456789012:role/terraform
    aws-region: us-east-1
- uses: google-github-actions/auth@v3
  if: env.TF_BD_CONTINUE == 'true'
  with:
    workload_identity_provider: projects/123/locations/global/workloadIdentityPools/github/providers/github
    service_account: terraform@project.iam.gserviceaccount.com
- uses: azure/login@v2
  if: env.TF_BD_CONTINUE == 'true'
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

4. Plan and Apply

Open a pull request with Terraform changes and comment:

.plan to dev

Pull request comment running .plan to dev

Review the plan comment. If it is correct, apply the saved plan:

Expanded Terraform change result in a pull request comment

.apply to dev

Pull request comment running .apply to dev

If new commits are pushed after planning, run .plan to dev again before applying.

Apply the saved plan

Normal apply uses the saved plan for the same environment and commit. It must not run a fresh untargeted Terraform apply.

After apply completes, the result is posted back to the pull request:

Terraform apply succeeded comment in GitHub

For a targeted plan, pass Terraform arguments after the pipe separator:

.plan to dev | -target=module.database

The matching apply command stays the same:

.apply to dev

The saved targeted plan is restored and applied. Terraform Branch Deploy rejects extra Terraform arguments on apply.

5. Roll Back

To apply the stable branch directly:

.apply main to prod

Rollback is intentionally separate from normal apply. It does not use a saved pull request plan.

Next