Skip to content

Automate Terraform Deployments: (1/2) Detect

This workflow is part of the Terraform-to-Azure deployment automation pipeline.
Its primary role is to detect changes to terraform.tfvars files in pull requests or direct pushes to the main branch, and then trigger targeted downstream processing for each changed file.


Purpose

  • Change Detection: Identify modifications to any terraform.tfvars file.
  • Targeted Execution: For each changed file, trigger a reusable downstream workflow to process, test, and deploy changes.
  • Efficiency: Avoid running unnecessary jobs when no .tfvars changes are detected.
  • Modularity: Enable environment-specific or file-specific CI/CD runs.

Trigger Conditions

The workflow runs when: - A push is made to the main branch and a .tfvars file changes. - A pull request is opened or updated and a .tfvars file changes.

on:
  push:
    branches: [main]
    paths: ["**/terraform.tfvars"]
  pull_request:
    paths: ["**/terraform.tfvars"]

Workflow Logic

Step 1 – Detect Changes

  • Uses a custom action (.github/actions/detect-changed-tfvars-files) to determine which .tfvars files have changed.
  • Handles both PR and main branch contexts.
  • Outputs a JSON array of changed file paths.

Step 2 – Early Exit

  • If no .tfvars files have changed, the workflow stops immediately to save resources.

Step 3 – Process Changes

  • If changes are detected:
  • A matrix job is created, with one job per changed .tfvars file.
  • Each job calls the reusable workflow .github/workflows/process-changed-tfvars-files.yaml.
  • Secrets are passed securely to the downstream workflow.

Concurrency Control

concurrency:
  group: terraform-checks-${{ github.ref }}
  cancel-in-progress: false
- Ensures only one run per branch/ref is active at a time. - Prevents overlapping runs that could cause deployment conflicts.


Security Considerations

  • Principle of Least Privilege: Only required secrets are passed to the downstream workflow.
  • Scoped Permissions:
    permissions:
      contents: write
      checks: write
      id-token: write
    
  • Secrets include:
  • GRINNTEC_TERRAFORM_DEPLOYMENTS_AZURE_PAT
  • AZURE_SUBSCRIPTION_ID
  • AZURE_TENANT_ID
  • AZURE_CLIENT_ID
  • INFRACOST_API_KEY

Key Components

Component Description
.github/actions/detect-changed-tfvars-files Custom action to detect changed .tfvars files.
.github/workflows/process-changed-tfvars-files.yaml Reusable workflow to process each changed file.
Matrix Strategy Runs jobs in parallel for each changed file.
Concurrency Group Prevents overlapping runs for the same branch.

Example Flow

  1. Developer updates environments/prod/terraform.tfvars in a PR.
  2. Workflow detects the change.
  3. Matrix job triggers process-changed-tfvars-files.yaml with:
    tfvars_file: environments/prod/terraform.tfvars
    
  4. Downstream workflow runs Terraform plan/apply for that environment.

Maintenance Notes

  • Update detection logic in .github/actions/detect-changed-tfvars-files if file structure changes.
  • Ensure .github/workflows/process-changed-tfvars-files.yaml supports all environments.
  • Review and rotate secrets regularly.

Full Workflow YAML

(Included for reference — matches the implemented configuration)

name: Detect Changed terraform.tfvars Files

permissions:
  contents: write
  checks: write
  id-token: write

on:
  push:
    branches: [main]
    paths: ["**/terraform.tfvars"]
  pull_request:
    paths: ["**/terraform.tfvars"]

concurrency:
  group: terraform-checks-${{ github.ref }}
  cancel-in-progress: false

jobs:
  detect-changed-tfvars-files:
    name: Detect Changed terraform.tfvars Files
    runs-on: ubuntu-latest
    outputs:
      changed_files: ${{ steps.detect-changed-tfvars-files.outputs.changed_tfvars }}
    steps:
      - name: Checking out repository
        run: echo "::notice::Checking out the repository with full history for change detection"

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

      - name: Sync to latest
        run: |
          git fetch origin main
          git checkout main
          git reset --hard origin/main

      - name: Detect Changed terraform.tfvars Files
        id: detect-changed-tfvars-files
        uses: ./.github/actions/detect-changed-tfvars-files/

      - name: Early Exit
        if: ${{ steps.detect-changed-tfvars-files.outputs.changed_tfvars == '' }}
        run: |
          echo "::notice::No changes detected in .tfvars files. Skipping further steps."
          exit 0

  process-tfvars-files:
    name: Process Changed terraform.tfvars File
    needs: detect-changed-tfvars-files
    if: ${{ needs.detect-changed-tfvars-files.outputs.changed_files != '' }}
    strategy:
      fail-fast: false
      matrix:
        tfvars_file: ${{ fromJson(needs.detect-changed-tfvars-files.outputs.changed_files) }}
    uses: ./.github/workflows/process-changed-tfvars-files.yaml
    with:
      tfvars_file: ${{ matrix.tfvars_file }}
    secrets:
      GRINNTEC_TERRAFORM_DEPLOYMENTS_AZURE_PAT: ${{ secrets.GRINNTEC_TERRAFORM_DEPLOYMENTS_AZURE_PAT }}
      AZURE_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
      AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
      AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
      INFRACOST_API_KEY: ${{ secrets.INFRACOST_API_KEY }}