atlantis-drift-detection

January 6, 2023 ยท View on GitHub

Detect terraform drift in atlantis

What it does

The general workflow of this repository is:

  1. Check out a mono repo of terraform code
  2. Find an atlantis.yaml file inside the repository
  3. Use atlantis to run /plan on each project in the atlantis.yaml file
  4. For each project with drift
    1. Trigger a GitHub workflow that can resolve the drift
    2. Comment the existence of the drift in slack
  5. For each project directory in the atlantis.yaml
    1. Run workspace list
    2. If any workspace isn't tracked by atlantis, notify slack

There is an optional flag to cache drift results inside DynamoDB, so we don't check the same directory twice in a short period of time.

Example for "Trigger a github workflow that can resolve the drift"

Here is the example we use to resolve a drift. This workflow touches a "trigger.txt" file that we configure all of our projects to listen to.

name: Force a terraform PR
on:
  workflow_dispatch:
    inputs:
      directory:
        description: >-
          Which directory to force a terraform workflow upon
        required: true
        type: string
jobs:
  plantrigger:
    runs-on: [self-hosted]
    name: Force terraform plan PR
    steps:
      # We use an application to make PRs, rather than hard code a user token
      - name: Generate token
        id: generate_token
        uses: peter-murray/workflow-application-token-action@v1
        with:
          application_id: $ {{ secrets.OUR_APP_ID_FOR_CREATING_PRS }}
          application_private_key: ${{ secrets.OUR_PEM_ID_FOR_CREATING_PRS }}
      - name: Checkout
        uses: actions/checkout@v2
      - run: ./scripts/update_trigger.sh ${DIR}
        env:
          DIR: ${{ github.event.inputs.directory }}
      - name: Create PR to terraform repo
        uses: peter-evans/create-pull-request@v3
        id: make-pr
        with:
          token: ${{ steps.generate_token.outputs.token }}
          branch: replan-${{ github.event.inputs.directory }}
          delete-branch: true
          title: "Force replan of directory ${{ github.event.inputs.directory }}"
          labels: forced-workflow
          committer: Forced Replan <noreply@cresta.ai>
          body: "A forced replan of this directory was triggered via github actions"
          commit-message: "Regenerated plan for ${{ github.event.inputs.directory }}"

Our script update_trigger.sh

#!/bin/bash
set -exou pipefail
# Modify the trigger.txt file of a directory which we expect to trigger an atlantis workflow
if [ ! -d "\$1" ]; then
  echo "Unable to find directory \$1"
  exit 1
fi
date > "\$1/trigger.txt"

Use as a github action

name: Drift detection
on:
  workflow_dispatch:
jobs:
  drift:
    name: detects drift
    runs-on: [self-hosted]
    steps:
      - name: detect drift
        uses: cresta/atlantis-drift-detection@v0.0.7
        env:
          ATLANTIS_HOST: atlantis.atlantis.svc.cluster.local
          ATLANTIS_TOKEN: ${{ secrets.ATLANTIS_TOKEN }}
          REPO: cresta/terraform-monorepo
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
          DYNAMODB_TABLE: atlantis-drift-detection
          WORKFLOW_OWNER: cresta
          WORKFLOW_REPO: terraform-monorepo
          WORKFLOW_ID: force_terraform_workflow.yaml
          WORKFLOW_REF: master
          GITHUB_APP_ID: 123456
          GITHUB_INSTALLATION_ID: 123456
          GITHUB_PEM_KEY: ${{ secrets.PR_CREATOR_PEM }}
          CACHE_VALID_DURATION: 168h

Configuration

Environment VariableDescriptionRequiredDefaultExample
REPOThe github repo to checkYescresta/terraform-monorepo
ATLANTIS_HOSTThe Hostname of the Atlantis serverYesatlantis.example.com
ATLANTIS_TOKENThe Atlantis API tokenYes1234567890
WORKFLOW_OWNERThe github owner of the workflow to trigger on driftNocresta
WORKFLOW_REPOThe github repo of the workflow to trigger on driftNoatlantis-drift-detection
WORKFLOW_IDThe ID of the workflow to trigger on driftNodrift.yaml
WORKFLOW_REFThe git ref to trigger the workflow onNomaster
DIRECTORY_WHITELISTA comma separated list of directories to checkNoterraform,modules
SLACK_WEBHOOK_URLThe Slack webhook URL to post updates toNohttps://hooks.slack.com/services/1234567890/1234567890/1234567890
SKIP_WORKSPACE_CHECKSkip checking if the workspace have driftedNofalsetrue
PARALLEL_RUNSThe number of parallel runs to useNo110
DYNAMODB_TABLEThe name of the DynamoDB table to use for caching resultsNoatlantis-drift-detectionatlantis-drift-detection
CACHE_VALID_DURATIONThe duration that previous results are still validNo24h180h
GITHUB_APP_IDAn application ID to use for github API callsNo123123
GITHUB_INSTALLATION_IDAn application install ID to use for github API callsNo123123
GITHUB_PEM_KEYA GitHub PEM key of an application, used to authenticate the app for API callsNo1231DEADBEAF....

Local development

Create a file named .env inside the root directory and populate it with the correct variables. Check out the example file or configuration for details. This file won't be checked in because it's inside the .gitignore.