Skip to main content

Terraform drift intelligence: detect, attribute, score, and fix IaC drift

Project description

๐Ÿ” drifty

Terraform drift intelligence โ€” detect what changed, who changed it, how dangerous it is, and how to fix it.

PyPI version Python CI License: MIT

pip install drifty

The Problem

terraform plan tells you what drifted. It doesn't tell you who changed it, how dangerous the change is, or what to do about it.

Manual changes in the AWS console during incidents, auto-scaling events, and ad-hoc CLI commands silently diverge your infrastructure from your Terraform state. By the time you notice, you don't know if it was a colleague, a runbook, or a security incident.

Enterprise platforms like Spacelift and HCP Terraform Cloud detect drift on a schedule โ€” but they're full IaC platforms that are heavyweight, expensive, and still offer zero attribution or severity intelligence.

drifty fills this exact gap.


Demo

$ drifty scan --workspace ./infra --attribute

๐Ÿ” drifty โ€” Terraform Drift Intelligence
Scanning workspace: ./infra  |  2026-06-05 14:00 UTC

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚  3 drifts detected  -   1 Critical  -   1 High  -   1 Low      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

๐Ÿ”ด CRITICAL  aws_security_group.main  (sg-0abc1234)
   Changed:  ingress.0.cidr_blocks  โ†’  ["0.0.0.0/0"]  (was: ["10.0.0.0/8"])
   Who:      arn:aws:iam::123456789:user/john.doe
   When:     2026-06-03 14:22:11 UTC
   Action:   ModifySecurityGroupRules
   Fix:      terraform import aws_security_group.main sg-0abc1234

๐ŸŸ  HIGH  aws_instance.api_server  (i-0def5678)
   Changed:  instance_type  โ†’  t3.large  (was: t3.medium)
   Who:      arn:aws:iam::123456789:role/ops-automation
   When:     2026-06-02 09:15:44 UTC
   Action:   ModifyInstanceAttribute
   Fix:      terraform import aws_instance.api_server i-0def5678

๐ŸŸข LOW  aws_s3_bucket.assets  (assets-bucket-prod)
   Changed:  tags.LastModified  โ†’  "2026-06-01"  (was: "2026-05-15")
   Who:      attribution unavailable (event outside 90-day CloudTrail window)
   Fix:      Add tag to Terraform config or run terraform apply to reconcile

โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
Run `drifty report --format markdown` to export this as a report.

Install

Requirements: Python 3.10+, Terraform 1.1+, AWS credentials configured

pip install drifty

Quick Start

# 1. Initialize config in your Terraform workspace
cd ./infra
drifty init

# 2. Scan for drift
drifty scan

# 3. Scan with CloudTrail attribution (who caused each drift)
drifty scan --attribute

# 4. Filter to critical and high only
drifty scan --severity high

# 5. Output as JSON (for CI/CD piping)
drifty scan --output json | jq '.findings[] | select(.severity=="critical")'

# 6. Export a markdown report
drifty report --format markdown --out ./drift-report.md

How It Works

drifty scan
    โ”‚
    โ”œโ”€ 1. runs: terraform plan -refresh-only -json
    โ”‚
    โ”œโ”€ 2. parses JSON Lines output โ†’ extracts resource_drift entries
    โ”‚
    โ”œโ”€ 3. scores each finding (scorer.py)
    โ”‚       critical โ†’ IAM, security groups, S3 policies
    โ”‚       high     โ†’ EC2 instances, RDS, load balancers
    โ”‚       medium   โ†’ Lambda, Auto Scaling, CloudWatch
    โ”‚       low      โ†’ tag-only changes
    โ”‚
    โ”œโ”€ 4. attributes each finding via CloudTrail (if --attribute)
    โ”‚       boto3 โ†’ LookupEvents by resource ID
    โ”‚       returns: IAM principal, timestamp, API action
    โ”‚
    โ””โ”€ 5. renders output
            terminal โ†’ Rich color-coded table
            json     โ†’ structured output for CI/CD
            markdown โ†’ report for Confluence / Notion

Commands

drifty scan

Options:
  --workspace PATH    Terraform directory (default: current dir)
  --profile TEXT      AWS CLI profile (default: "default")
  --attribute         Enable CloudTrail attribution
  --severity TEXT     Minimum severity: critical | high | medium | low
  --output TEXT       Output format: terminal | json | markdown
  --notify TEXT       Send results to: slack

drifty init

Initializes .drifty/config.yaml in the workspace with default settings.

drifty config set KEY=VALUE

drifty config set default_severity=high
drifty config set default_profile=prod
drifty config set slack_webhook=https://hooks.slack.com/...
drifty config set cloudtrail_lookback_days=30

drifty report

drifty report --format markdown --out ./reports/drift-$(date +%F).md
drifty report --format json

Severity Rules

Resource Type Severity
aws_iam_role_policy, aws_iam_policy ๐Ÿ”ด Critical
aws_security_group, aws_security_group_rule ๐Ÿ”ด Critical
aws_s3_bucket_policy, aws_s3_bucket_public_access_block ๐Ÿ”ด Critical
aws_instance (type/AMI change) ๐ŸŸ  High
aws_rds_instance, aws_lb, aws_alb ๐ŸŸ  High
aws_lambda_function, aws_autoscaling_group ๐ŸŸก Medium
aws_cloudwatch_metric_alarm ๐ŸŸก Medium
aws_instance (tag-only change) ๐ŸŸข Low
aws_s3_bucket (tag-only change) ๐ŸŸข Low

Override any rule per-workspace:

# .drifty/config.yaml
severity_overrides:
  aws_lambda_function: high
  aws_cloudwatch_metric_alarm: low

drifty vs. the Alternatives

Feature terraform plan Spacelift / HCP TF drifty
Detects drift โœ… โœ… โœ…
Who caused it โŒ โŒ โœ… CloudTrail
Severity score โŒ โŒ โœ…
Remediation hint โŒ โŒ โœ…
JSON / Markdown output โŒ partial โœ…
Works locally / in CI โœ… โŒ SaaS only โœ…
Cost free $$$ free
Install N/A platform setup pip install drifty

Configuration Reference

# .drifty/config.yaml
default_profile: default          # AWS CLI profile
default_severity: null            # minimum severity filter (null = show all)
default_output: terminal          # terminal | json | markdown
slack_webhook: null               # Slack incoming webhook URL
cloudtrail_lookback_days: 90      # max 90 (CloudTrail API limit)
severity_overrides: {}            # per-resource type overrides

Roadmap

  • --notify slack โ€” post drift summary to Slack webhook
  • drifty watch โ€” continuous drift monitoring on a poll interval
  • Azure and GCP provider support
  • GitHub PR comment integration โ€” post drift report as a PR comment
  • Drift history โ€” persist findings to .drifty/history.json
  • drifty ignore โ€” suppress known/accepted drift entries

Contributing

git clone https://github.com/satyajit-dey-17/drifty.git
cd drifty
poetry install
poetry run pytest -v
poetry run ruff check drifty/
poetry run black drifty/

Please open an issue before submitting a large PR. See .github/ISSUE_TEMPLATE/ for bug and feature templates.


License

MIT ยฉ Satyajit Dey

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

drifty-0.1.0.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

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

drifty-0.1.0-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

File details

Details for the file drifty-0.1.0.tar.gz.

File metadata

  • Download URL: drifty-0.1.0.tar.gz
  • Upload date:
  • Size: 22.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for drifty-0.1.0.tar.gz
Algorithm Hash digest
SHA256 0de284994802ccb1ac2f2ad24993298e89ad361bf7172447ba7f7c3346dee40b
MD5 42396c3b954fa15f1785ae627542fc47
BLAKE2b-256 f361451d6bdcfb3c42b25078a7f067460793e393c30f7f3494d2a6d87e1dddb0

See more details on using hashes here.

Provenance

The following attestation bundles were made for drifty-0.1.0.tar.gz:

Publisher: publish.yml on satyajit-dey-17/drifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file drifty-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: drifty-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 23.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for drifty-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5bc412f39d853f78f606e885b7d67f9159121a7a5cc29028baf47820fc22f5b9
MD5 2612af29eea67c9ba16d20b2f16641fa
BLAKE2b-256 6b117c11d11b9ccf4499e48589a9038e0e89dff5de9ba2add62b6695aa1828b5

See more details on using hashes here.

Provenance

The following attestation bundles were made for drifty-0.1.0-py3-none-any.whl:

Publisher: publish.yml on satyajit-dey-17/drifty

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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