Skip to content
Back to skills

Drift Detection

ASecurity

Detect, classify, and automate Terraform drift detection in CI — scheduled plans, drift metrics, cloud-native audit log correlation.

  • 19 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
ai-agentsbashawsgcpterraformgitapidatabasebackendsecurity

Works with

  • api

Security analysis

A100/100

Scanned October 19, 2026

npx -y skills add sawrus/agent-guides --skill drift-detection --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Drift Detection?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Drift Detection
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/sawrus-drift-detection/badge)](https://www.skillsdirectory.com/skills/sawrus-drift-detection)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: drift-detection
type: skill
description: Detect, classify, and automate Terraform drift detection in CI — scheduled plans, drift metrics, cloud-native audit log correlation.
related-rules:
  - immutability.md
  - iac-standards.md
allowed-tools: Read, Write, Edit, Bash
---

# Skill: Drift Detection

> **Expertise:** Terraform plan-based drift detection, CI scheduling, cloud audit log correlation, drift classification.

## When to load

When setting up scheduled drift detection, investigating detected drift, or correlating drift with cloud audit events.

## Scheduled Drift Detection (GitHub Actions)

```yaml
# .github/workflows/drift-detection.yml
name: Drift Detection

on:
  schedule:
    - cron: '0 */6 * * *'   # every 6 hours
  workflow_dispatch:          # manual trigger

jobs:
  detect-drift:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        component: [network, k8s-cluster, databases, iam-roles]
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.DRIFT_DETECTOR_ROLE_ARN }}
          aws-region: eu-west-1

      - name: Terraform init
        working-directory: terraform/environments/production/${{ matrix.component }}
        run: terraform init -backend-config=backend.hcl

      - name: Terraform plan (drift check)
        id: plan
        working-directory: terraform/environments/production/${{ matrix.component }}
        run: |
          terraform plan \
            -var-file=terraform.tfvars \
            -detailed-exitcode \
            -out=drift-check.plan \
            2>&1 | tee drift-output.txt
          echo "exit_code=$?" >> $GITHUB_OUTPUT
        continue-on-error: true    # exit 2 = changes, don't fail job

      - name: Classify drift
        if: steps.plan.outputs.exit_code == '2'
        run: |
          # Check for security-sensitive resource changes
          if grep -E "aws_iam|aws_security_group|aws_kms|encryption" drift-output.txt; then
            echo "SEVERITY=INVESTIGATE" >> $GITHUB_ENV
          else
            echo "SEVERITY=REMEDIATE" >> $GITHUB_ENV
          fi

      - name: Create GitHub Issue on drift
        if: steps.plan.outputs.exit_code == '2'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const plan = fs.readFileSync('terraform/environments/production/${{ matrix.component }}/drift-output.txt', 'utf8');
            await github.rest.issues.create({
              owner: context.repo.owner,
              repo: context.repo.repo,
              title: `[DRIFT] ${{ matrix.component }} — ${process.env.SEVERITY}`,
              body: `## Drift detected in \`${{ matrix.component }}\`\n\n**Severity:** ${process.env.SEVERITY}\n\n\`\`\`\n${plan.slice(0, 3000)}\n\`\`\``,
              labels: ['infrastructure', 'drift', process.env.SEVERITY.toLowerCase()]
            });

      - name: Alert on INVESTIGATE drift
        if: env.SEVERITY == 'INVESTIGATE'
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {"text": "🚨 *INVESTIGATE drift* in `${{ matrix.component }}` — may indicate unauthorized change. <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View>"}
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_ONCALL_WEBHOOK }}
```

## GitLab CI Scheduled Drift

```yaml
# .gitlab-ci.yml — drift detection job (triggered by schedule)
drift-detect:
  stage: audit
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
  script:
    - cd terraform/environments/production/${COMPONENT}
    - terraform init -backend-config=backend.hcl
    - terraform plan -var-file=terraform.tfvars -detailed-exitcode
      -out=drift.plan 2>&1 | tee drift-output.txt || EXITCODE=$?
    - |
      if [ "${EXITCODE}" == "2" ]; then
        echo "DRIFT DETECTED in ${COMPONENT}"
        # Post to Slack, create MR, or alert via API
        curl -X POST "$SLACK_WEBHOOK" \
          -H 'Content-type: application/json' \
          --data "{\"text\":\"Drift in ${COMPONENT}\"}"
        exit 1
      fi
  parallel:
    matrix:
      - COMPONENT: [network, k8s-cluster, databases]
```

## Drift Classification Logic

```bash
# Parse terraform plan output for drift classification
classify_drift() {
  local plan_output="$1"

  # INVESTIGATE: security-sensitive resources changed
  if echo "$plan_output" | grep -qE \
    "aws_iam_policy|aws_security_group|kms_key|aws_s3_bucket_server_side_encryption|encryption_at_rest"; then
    echo "INVESTIGATE"
    return
  fi

  # INVESTIGATE: resources deleted unexpectedly
  if echo "$plan_output" | grep -q "# .* will be destroyed"; then
    echo "INVESTIGATE"
    return
  fi

  # REMEDIATE: configuration drift (non-security)
  echo "REMEDIATE"
}
```

## Correlating Drift with Cloud Audit Logs

```bash
# AWS CloudTrail: who changed the drifted resource?
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=<resource-id> \
  --start-time $(date -d '-24 hours' --iso-8601) \
  --query 'Events[*].{User: Username, Time: EventTime, Event: EventName}' \
  --output table

# GCP Audit Logs
gcloud logging read \
  'resource.type="gce_instance" AND protoPayload.methodName:"compute.instances"' \
  --freshness=24h \
  --format='table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.methodName)'
```

## Drift Suppression (accepted exceptions)

```hcl
# lifecycle ignore_changes for intentionally managed-outside-TF fields
resource "aws_autoscaling_group" "workers" {
  # ...
  lifecycle {
    ignore_changes = [
      desired_capacity,    # managed by cluster autoscaler, not TF
      min_size,
    ]
  }
}
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.