Skip to main content

Continuous Terraform drift detection, reporting, and auto-remediation CLI

Project description

tfdrift

Continuous Terraform drift detection, reporting, and auto-remediation.

[Python 3.9+](https://www.python.org/downloads/ PyPI version License: Apache-2.0 CI

tfdrift demo


tfdrift is a Python CLI tool that monitors your Terraform-managed infrastructure for drift — changes made outside of Terraform workflows (console clicks, scripts, other tools). It scans your Terraform workspaces, detects discrepancies between state and reality, generates structured reports, and optionally auto-remediates or notifies your team.

Why tfdrift? The most popular open-source drift detection tool (driftctl) has been in maintenance mode since mid-2023. Enterprise solutions like Terraform Enterprise cost $15K+/year. tfdrift fills the gap: a free, modern, actively maintained CLI that does one thing well.

Features

  • Multi-workspace scanning — Recursively discovers all Terraform workspaces in a directory tree
  • Structured drift reports — JSON, Markdown, or human-readable table output
  • Severity classification — Categorizes drift by risk level (critical/high/medium/low) based on resource type and attribute
  • Slack & webhook notifications — Get alerted the moment drift is detected
  • Auto-remediation — Optionally run terraform apply to fix drift (with safety guards)
  • CI/CD friendly — Exit codes, JSON output, and GitHub Actions integration out of the box
  • Watch mode — Continuously monitor for drift on a schedule
  • Ignore rules — Filter out known/expected drift with .tfdriftignore

Quick start

Install

pip install tfdrift

Scan for drift

# Scan current directory for all Terraform workspaces
tfdrift scan

# Scan a specific directory
tfdrift scan --path /path/to/terraform

# Output as JSON
tfdrift scan --format json

# Output as Markdown report
tfdrift scan --format markdown --output drift-report.md

Try it locally (no cloud credentials needed)

Simulate drift using Terraform's null provider and a local backend:

# 1. Create a workspace and apply initial state
mkdir -p /tmp/tfdrift-demo && cat > /tmp/tfdrift-demo/main.tf <<'EOF'
terraform {
  required_providers {
    null = { source = "hashicorp/null", version = "~> 3.0" }
  }
  backend "local" {}
}

resource "null_resource" "server" {
  triggers = { instance_type = "t3.micro", region = "us-east-1" }
}

resource "null_resource" "db" {
  triggers = { engine = "postgres", version = "14" }
}
EOF

cd /tmp/tfdrift-demo && terraform init && terraform apply -auto-approve

# 2. Simulate drift — change config without updating state
sed -i 's/t3.micro/t3.large/; s/us-east-1/us-west-2/; s/version = "14"/version = "15"/' main.tf

# 3. Run tfdrift
tfdrift scan --path /tmp/tfdrift-demo

Expected output:

⚠️  Drift detected: 2 resource(s) across 1/1 workspace(s)

🟡 medium: 2

📂 /tmp/tfdrift-demo (2 drifted, 0.3s)
┏━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━┓
┃ Severity ┃ Resource              ┃ Action  ┃ Changed attributes ┃
┡━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━┩
│ MEDIUM   │ null_resource.db      │ replace │ id, triggers       │
│ MEDIUM   │ null_resource.server  │ replace │ id, triggers       │
└──────────┴───────────────────────┴─────────┴────────────────────┘

Watch mode (continuous monitoring)

# Check every 30 minutes, notify Slack on drift
tfdrift watch --interval 30m --slack-webhook https://hooks.slack.com/services/XXX

Auto-remediate

# Auto-fix drift in dev (requires --confirm for safety)
tfdrift scan --auto-fix --confirm --env dev

# Dry run — show what would be fixed
tfdrift scan --auto-fix --dry-run

Configuration

Create a .tfdrift.yml in your project root:

# .tfdrift.yml
scan:
  paths:
    - ./infrastructure
    - ./modules
  exclude:
    - "**/test/**"
    - "**/.terraform/**"
  # Auto-detect .tfvars files in each workspace (default: true)
  auto_detect_var_files: true
  # Or specify explicit var files
  var_files:
    - envs/dev.tfvars
  # Or pass variables directly
  vars:
    environment: production
    region: us-east-1

severity:
  critical:
    - aws_security_group.*.ingress
    - aws_iam_policy.*.policy
    - aws_s3_bucket.*.acl
  high:
    - aws_instance.*.instance_type
    - aws_rds_instance.*.engine_version

notifications:
  slack:
    webhook_url: ${SLACK_WEBHOOK_URL}
    channel: "#infra-alerts"
    min_severity: high
  webhook:
    url: ${WEBHOOK_URL}
    method: POST

remediation:
  auto_fix: false
  allowed_environments:
    - dev
    - staging
  require_approval: true
  max_changes: 5  # safety limit

ignore:
  # Ignore expected drift
  - resource: aws_autoscaling_group.*
    attribute: desired_capacity
  - resource: aws_ecs_service.*
    attribute: desired_count

Ignore rules

Create a .tfdriftignore file to skip known drift:

# Autoscaling changes are expected
aws_autoscaling_group.*.desired_capacity
aws_ecs_service.*.desired_count

# Tags managed by external system
*.tags.LastModified
*.tags.UpdatedBy

CI/CD Integration

GitHub Actions

# .github/workflows/drift-check.yml
name: Terraform Drift Check
on:
  schedule:
    - cron: '0 */6 * * *'  # Every 6 hours
  workflow_dispatch:

jobs:
  drift-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hashicorp/setup-terraform@v3
      - uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      - run: pip install tfdrift
      - run: tfdrift scan --format json --output drift-report.json
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: drift-report
          path: drift-report.json

GitLab CI

drift-check:
  image: python:3.11
  before_script:
    - pip install tfdrift
    - apt-get update && apt-get install -y terraform
  script:
    - tfdrift scan --format json --output drift-report.json
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
  artifacts:
    paths:
      - drift-report.json
    when: on_failure

Exit codes

Code Meaning
0 No drift detected
1 Drift detected
2 Error during scan
3 Drift detected and auto-remediated

Architecture

tfdrift/
├── commands/        # CLI command handlers (scan, watch, init)
├── detectors/       # Drift detection engine (terraform plan parser)
├── reporters/       # Output formatters (JSON, Markdown, table, Slack)
├── remediators/     # Auto-fix logic with safety guards
├── config.py        # Configuration loader (.tfdrift.yml)
├── models.py        # Data models (DriftResult, Resource, etc.)
├── severity.py      # Severity classification engine
└── cli.py           # CLI entry point (Click)

Comparison with alternatives

Feature tfdrift driftctl (archived) terraform plan Terraform Enterprise
Active maintenance ❌ (since 2023)
Multi-workspace scan
Severity classification
Auto-remediation
Slack/webhook alerts
Watch mode
Ignore rules
Cost Free Free Free $15K+/yr
Language Python Go Go (HCL) Proprietary

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

# Development setup
git clone https://github.com/sudarshan8417/tfdrift.git
cd tfdrift
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
pytest

License

Apache License 2.0 — see LICENSE for details.

Acknowledgments

Inspired by driftctl and the Terraform community's need for maintained, open-source drift detection tooling.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tfdrift-0.2.2.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tfdrift-0.2.2-py3-none-any.whl (26.6 kB view details)

Uploaded Python 3

File details

Details for the file tfdrift-0.2.2.tar.gz.

File metadata

  • Download URL: tfdrift-0.2.2.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.1

File hashes

Hashes for tfdrift-0.2.2.tar.gz
Algorithm Hash digest
SHA256 6f50b99e4801317fa08f5bb0ce3c9920456bfb124d71b927f0fd0087f35d1a30
MD5 5c323347447d669e34d872e8b4765d3e
BLAKE2b-256 7a142f88efc4b691131e3c4b28a49bad3d69f6beb3c245d6c3582922f3389a78

See more details on using hashes here.

File details

Details for the file tfdrift-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: tfdrift-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 26.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.1

File hashes

Hashes for tfdrift-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 0730c50830d0ca29e0a68768d7e6710606062f4df0528e0009aec23dc80148f2
MD5 f5a002f23f465c73596fa241c909655d
BLAKE2b-256 32205624f10c51af8fa88ca3050a6ebbf987d0b17ef76751990e958a034c26b6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page