Skip to content

Run a Drift Check in GitHub Actions

The following workflows are blueprints and need some adjustments to work for you.

Search for CHANGEME to adjust needed credentials details for AWS, Google Cloud, and Azure examples.

Drift Checks require action and protocolling the results, so Terramate Cloud support is required for those workflows at the moment.

The following workflow examples run every day at 2 am.

Terramate Cloud support

When synchronizing drift checks to Terramate Cloud, the following features will support the team with handling drifts:

  • Get notified on new drifts via Slack notifications.
  • Highlight and identify drifted stacks in the Stacks List and Dashboard.
  • See drift details without requiring your team to have elevated access to read the Terraform state or have access to read the cloud resources.
  • Identify the time when a drift happened and how long a stack stayed in a drifted state.
  • Create automation to reconcile a drift without human interaction using the --status filter in Terramate CLI.

Deployment Blueprints

Create the following GitHub Actions configuration at .github/workflows/drift.yml

Please select the tab that fits your use case. Currently available use cases are:

  • Terraform + Terramate Cloud
  • OpenTofu + Terramate Cloud
  • Terragrunt + Terramate Cloud
yml
name: Scheduled Terraform Drift Detection

on:
  schedule:
    - cron: '0 2 * * *'

jobs:
  drift-detection:
    name: Check Drift

    runs-on: ubuntu-latest

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

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0

      - name: Install Terramate
        uses: terramate-io/terramate-action@v2

      - name: Install Terraform
        uses: hashicorp/setup-terraform@v3
        with:
          terraform_version: 1.7.4
          terraform_wrapper: false

      ## Comment this step out if not using AWS
      - name: Configure AWS credentials via OIDC
        id: auth
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: CHANGEME_AWS_REGION
          role-to-assume: CHANGEME_IAM_ROLE_ARN

      ## Uncomment this if using Google Cloud
      # - name: Authenticate to Google Cloud
      #   id: auth
      #   uses: google-github-actions/auth@v2
      #   with:
      #     workload_identity_provider: CHANGEME_WORKLOAD_IDENTITY_PROVIDER
      #     service_account: CHANGEME_SERVICE_ACCOUNT_EMAIL

      ## Uncomment this if using Microsoft Azure
      # - name: Configure Azure credentials
      #   id: auth
      #   uses: azure/login@v2
      #   with:
      #     client-id: CHANGEME_AZURE_CLIENT_ID
      #     tenant-id: CHANGEME_AZURE_TENANT_ID
      #     subscription-id: CHANGEME_AZURE_SUBSCRIPTION_ID

      - name: Initialize Terraform
        id: init
        run: |
          terramate run \
           --parallel 1 \
           -- \
           terraform init -lock-timeout=5m

      - name: Run drift detection
        id: drift
        run: |
          terramate run \
          --sync-drift-status \
          --terraform-plan-file=drift.tfplan \
          --continue-on-error \
          --parallel 5 \
          -- \
          terraform plan -out drift.tfplan -detailed-exitcode -lock=false
        env:
          GITHUB_TOKEN: ${{ github.token }}

      ## Optional step to reconcile (apply) the drifted stacks
      ## This only applies to stack with a `reconcile` tag by default
      ## Uncomment if desired
      # - name: Drift reconciliation
      #   id: drift-reconcile
      #   run: |
      #     terramate run \
      #       --status=drifted \
      #       --tags reconcile
      #       --parallel 5 \
      #       --sync-deployment \
      #       --terraform-plan-file=drift.tfplan \
      #       -- \
      #       terraform apply -input=false -auto-approve -lock-timeout=5m drift.tfplan
      #   env:
      #     GITHUB_TOKEN: ${{ github.token }}
yml
name: Scheduled OpenTofu Drift Detection

on:
  schedule:
    - cron: '0 2 * * *'

jobs:
  drift-detection:
    name: Check Drift

    runs-on: ubuntu-latest

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

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0

      - name: Install Terramate
        uses: terramate-io/terramate-action@v2

      - name: Install OpenTofu
        uses: opentofu/setup-opentofu@v1
        with:
          tofu_version: 1.9.0
          tofu_wrapper: false

      ## Comment this step out if not using AWS
      - name: Configure AWS credentials via OIDC
        id: auth
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: CHANGEME_AWS_REGION
          role-to-assume: CHANGEME_IAM_ROLE_ARN

      ## Uncomment this if using Google Cloud
      # - name: Authenticate to Google Cloud
      #   id: auth
      #   uses: google-github-actions/auth@v2
      #   with:
      #     workload_identity_provider: CHANGEME_WORKLOAD_IDENTITY_PROVIDER
      #     service_account: CHANGEME_SERVICE_ACCOUNT_EMAIL

      ## Uncomment this if using Microsoft Azure
      # - name: Configure Azure credentials
      #   id: auth
      #   uses: azure/login@v2
      #   with:
      #     client-id: CHANGEME_AZURE_CLIENT_ID
      #     tenant-id: CHANGEME_AZURE_TENANT_ID
      #     subscription-id: CHANGEME_AZURE_SUBSCRIPTION_ID

      - name: Initialize OpenTofu
        id: init
        run: |
          terramate run \
           --parallel 1 \
           -- \
           tofu init -lock-timeout=5m

      - name: Run drift detection
        id: drift
        run: |
          terramate run \
            --parallel 5 \
            --sync-drift-status \
            --continue-on-error \
            --tofu-plan-file=drift.otplan \
            -- \
            tofu plan -out drift.otplan -detailed-exitcode -lock=false
        env:
          GITHUB_TOKEN: ${{ github.token }}

      ## Optional step to reconcile (apply) the drifted stacks
      ## This only applies to stack with a `reconcile` tag by default
      ## Uncomment if desired
      # - name: Drift reconciliation
      #   id: drift-reconcile
      #   run: |
      #     terramate run \
      #       --status=drifted \
      #       --tags reconcile
      #       --parallel 5 \
      #       --sync-deployment \
      #       --tofu-plan-file=drift.otplan \
      #       -- \
      #       tofu apply -input=false -auto-approve -lock-timeout=5m drift.otplan
      #   env:
      #     GITHUB_TOKEN: ${{ github.token }}
yml
name: Scheduled Terragrunt Drift Detection

on:
  schedule:
    - cron: '0 2 * * *'

jobs:
  drift-detection:
    name: Check Drift

    runs-on: ubuntu-latest

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

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ github.head_ref }}
          fetch-depth: 0

      - name: Install Terramate
        uses: terramate-io/terramate-action@v2

      - name: Setup Terragrunt
        uses: autero1/action-terragrunt@v3
        with:
          terragrunt-version: 0.72.6
          token: ${{ github.token }}

      ## Comment this step out if not using AWS
      - name: Configure AWS credentials via OIDC
        id: auth
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: CHANGEME_AWS_REGION
          role-to-assume: CHANGEME_IAM_ROLE_ARN

      ## Uncomment this if using Google Cloud
      # - name: Authenticate to Google Cloud
      #   id: auth
      #   uses: google-github-actions/auth@v2
      #   with:
      #     workload_identity_provider: CHANGEME_WORKLOAD_IDENTITY_PROVIDER
      #     service_account: CHANGEME_SERVICE_ACCOUNT_EMAIL

      ## Uncomment this if using Microsoft Azure
      # - name: Configure Azure credentials
      #   id: auth
      #   uses: azure/login@v2
      #   with:
      #     client-id: CHANGEME_AZURE_CLIENT_ID
      #     tenant-id: CHANGEME_AZURE_TENANT_ID
      #     subscription-id: CHANGEME_AZURE_SUBSCRIPTION_ID

      - name: Initialize Terragrunt
        id: init
        run: |
          terramate run \
           --parallel 1 \
           -- \
           terragrunt init -lock-timeout=5m

      - name: Run drift detection
        id: drift
        run: |
          terramate run \
            --parallel 5 \
            --sync-drift-status \
            --continue-on-error \
            --terraform-plan-file=drift.tfplan \
            --terragrunt \
            -- \
            terragrunt plan -out drift.tfplan -detailed-exitcode -lock=false
        env:
          GITHUB_TOKEN: ${{ github.token }}
      
      ## Optional step to reconcile (apply) the drifted stacks
      ## This only applies to stack with a `reconcile` tag by default
      ## Uncomment if desired
      # - name: Drift reconciliation
      #   id: drift-reconcile
      #   run: |
      #     terramate run \
      #       --status=drifted \
      #       --tags reconcile
      #       --parallel 5 \
      #       --sync-deployment \
      #       --terraform-plan-file=drift.tfplan \
      #       --terragrunt \
      #       -- \
      #       terragrunt apply -input=false -auto-approve -lock-timeout=5m drift.tfplan
      #   env:
      #     GITHUB_TOKEN: ${{ github.token }}