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.1.tar.gz (28.8 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.1-py3-none-any.whl (26.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: tfdrift-0.2.1.tar.gz
  • Upload date:
  • Size: 28.8 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.1.tar.gz
Algorithm Hash digest
SHA256 0ac6af1d2acf81cb28c5b26b67a821d4b31dd47176b5e73c4220445a25c3dbcc
MD5 d417dc1f3a640ec43292ecaefcae78a8
BLAKE2b-256 d3ae8e7c0e3e46d882ca5f2d15289ad027df3e8aaaf9e05a5d0f8ce77170ed36

See more details on using hashes here.

File details

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

File metadata

  • Download URL: tfdrift-0.2.1-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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 9d85052d61e7e32c827ab0127932e0741861450157970867e7ce6f6d90c6eddd
MD5 d4a47251b3cde9a2334e4bb31cfbb09f
BLAKE2b-256 48a4b3e9132b02915b595fd6a3d8fb4d6b4bc411aecfd48e0153607f6e5afe35

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