{"id":17961006,"url":"https://github.com/slok/tfe-drift","last_synced_at":"2025-03-25T03:31:28.457Z","repository":{"id":63200944,"uuid":"564654960","full_name":"slok/tfe-drift","owner":"slok","description":"Automated Terraform cloud and enterprise drift detection","archived":false,"fork":false,"pushed_at":"2024-05-07T09:53:08.000Z","size":475,"stargazers_count":37,"open_issues_count":9,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-19T08:39:19.967Z","etag":null,"topics":["drift","drift-detection","terraform","terraform-cloud","tf","tfcloud","tfe"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/slok.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-11-11T07:24:41.000Z","updated_at":"2024-09-28T20:48:11.000Z","dependencies_parsed_at":"2024-05-07T10:59:47.031Z","dependency_job_id":null,"html_url":"https://github.com/slok/tfe-drift","commit_stats":{"total_commits":52,"total_committers":2,"mean_commits":26.0,"dds":"0.11538461538461542","last_synced_commit":"0a38d7a96cca012fd8216299c69eca9631069a19"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slok%2Ftfe-drift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slok%2Ftfe-drift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slok%2Ftfe-drift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slok%2Ftfe-drift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slok","download_url":"https://codeload.github.com/slok/tfe-drift/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245394746,"owners_count":20608122,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["drift","drift-detection","terraform","terraform-cloud","tf","tfcloud","tfe"],"created_at":"2024-10-29T11:08:02.664Z","updated_at":"2025-03-25T03:31:28.449Z","avatar_url":"https://github.com/slok.png","language":"Go","funding_links":[],"categories":["Evidence and Audit"],"sub_categories":["Drift Detection"],"readme":"\u003cp align=\"center\"\u003e\n    \u003cimg src=\"docs/img/logo.png\" width=\"20%\" align=\"center\" alt=\"tfe-drift\"\u003e\n\u003c/p\u003e\n\n# tfe-drift\n\n[![CI](https://github.com/slok/tfe-drift/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/slok/tfe-drift/actions/workflows/ci.yaml)\n[![Go Report Card](https://goreportcard.com/badge/github.com/slok/tfe-drift)](https://goreportcard.com/report/github.com/slok/tfe-drift)\n[![Apache 2 licensed](https://img.shields.io/badge/license-Apache2-blue.svg)](https://raw.githubusercontent.com/slok/tfe-drift/master/LICENSE)\n[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/slok/tfe-drift)](https://github.com/slok/tfe-drift/releases/latest)\n\n## Introduction\n\nAutomated Terraform Cloud/Enterprise drift detection.\n\n## Features\n\n- Automate the execution of drift detection plans.\n- Limit executed drift detection plans (used to avoid long plan queues with available workers).\n- Sort drift detection plans by previous detections age.\n- Filter drift detections by workspace.\n- Ignore if drift detection plan not required (already running, executed recently...)\n- Result of the detection plans summary as output to automate with other apps.\n- Two running modes: controller mode (intervals), single run (for CI and crons).\n- Easy to automate with CI (It comes with a ready to use [Github action][tfe-drift-gh-actions]).\n- Prometheus metrics exporter for drift detections (in controller mode).\n- Compatible with Terraform Cloud and Terraform Enterprise.\n- Easy and simple to use.\n\n## Getting started\n\n2 variables are required by default:\n\n- Terraform cloud/enterprise API token: Use `--tfe-token` or `TFE_DRIFT_TFE_TOKEN` env var.\n- Terraform cloud/enterprise organization: Use `--tfe-organization` or `TFE_DRIFT_TFE_ORGANIZATION` env var.\n\n### Single run mode\n\n```bash\ntfe-drift run --limit-max-plans 5\n```\n\n### Controller mode\n\n```bash\ntfe-drift controller --limit-max-plans 5\n```\n\n## Install\n\n### Binary\n\nGet the binary from the [releases](https://github.com/slok/tfe-drift/releases).\n\n### Docker\n\nYou can use the released [docker images](https://github.com/slok/tfe-drift/pkgs/container/tfe-drift).\n\n```bash\ndocker run --rm -it -e TFE_DRIFT_TFE_TOKEN=${TFE_DRIFT_TFE_TOKEN} ghcr.io/slok/tfe-drift:latest run --help\n```\n\n## Usage\n\n### Controller\n\nIf you want to let tfe-drift run as a long-running process triggering drift-detections at regular intervals, use `controller` mode.\n\n```bash\ntfe-drift controller --detect-interval 5m --limit-max-plan 1\n```\n\n### Single run with github actions\n\nYou can use [tfe-drift github action][tfe-drift-gh-actions]\n\n```yaml\nname: drift-detection\n\non:\n  schedule:\n    - cron:  '0 * * * *' # Every hour.\n\njobs:\n  drift-detection:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: slok/tfe-drift-action@v0.3.0\n        id: tfe-drift\n        with:\n          tfe-token: ${{ secrets.TFE_TOKEN }}\n          tfe-org: slok\n          limit-max-plans: 3 # Avoid queuing lots of speculative plans.\n          not-before: 24h # A drift detection per day it's enough.\n```\n\nGithub action will write a job summary with the executed drift detections results:\n\n![Drift detection results job summary](docs/img/job-summary.png)\n\n### Use cases\n\nExecute single run in dry run mode to see what would be the workspaces affected:\n\n```bash\ntfe-drift run --dry-run\n```\n\nExecute single run with safe defaults and get the result output in JSON:\n\n```bash\ntfe-drift run -o json\n```\n\nExecute single run limiting to a max of 2 executed plans, ignore workspace drift detections that have been already executed in the last 2h, and exclude dns workspace:\n\n```bash\ntfe-drift run --exclude dns --not-before 2h --limit-max-plan 2\n```\n\nExecute the controller with an interval of 5m with a limit of 1 on the workspaces labelled with `enable-drift-detection`:\n\n```bash\ntfe-drift controller --detect-interval 5m --limit-max-plan 1 --include-tag enable-drift-detection\n```\n\nExecute the controller as only prometheus metrics exporter:\n\n```bash\ntfe-drift controller --disable-drift-detector\n```\n\n## Metrics\n\nAs an example lets use this alerting rule query that alerts on drifts happened in the last half hour:\n\n```promql\nmax by (organization_name, workspace_name, run_url) (\n    (\n        tfe_drift_workspace_drift_detection_state{state=\"drift\"} \u003e 0\n      and on (workspace_name)\n        (time() - tfe_drift_workspace_drift_detection_create) \u003c 1800\n    )\n  * on (workspace_name) group_right ()\n    tfe_drift_workspace_info\n)\n```\n\nExplanation:\n\n- `tfe_drift_workspace_drift_detection_state{state=\"drift\"} \u003e 0`: Give me the workspaces  that are in `drift` state (we could ask also for `drift_plan_error`).\n- `(time() - tfe_drift_workspace_drift_detection_create) \u003c 1800`: Give me the workspaces that had a drift detection in the last `30m`.\n- `* on (workspace_name) group_right () tfe_drift_workspace_info`: Add all the labels to the workspaces that meet the previous queries (state and recently drift detection).\n- `max by (organization_name, workspace_name, run_url)`: We only want those 3 labels, so we drop them by using aggregation (we could use, `min`, `sum`... doesn't matter as we don't use the value).\n\n## F.A.Q\n\n### How is a drift detection executed?\n\nIt's a terraform [speculative plan](https://developer.hashicorp.com/terraform/cloud-docs/run/remote-operations#speculative-plans) using the configured latest terraform workspace source code (normally `main` branch on the specified repository and directory).\n\n### How does it work?\n\nWhen tfe-drift is executed it runs with an specific identifier (`--app-id`, by default `tfe-drift`).\n\nAll the drift detections that tfe-drift runs using TFE API, will be identified by this ID on the plan message.\n\nFrom now on, it will use this ID to get the workspaces drift detection plans and use these information to decide the result or if needs to run again or not on next executions.\n\nIf the executed drift detection terraform plan has changes, its a drift!\n\n### Why? Hashicorp recently announced [Drift detection][drift-detection]\n\nTerraform cloud offers it's own [drift detector][drift-detection](Looks awesome!), however, this feature it's not available for non \"Business\" tiers.\n\n### Why limit the plans?\n\nSometimes Terraform cloud execution workers are busy or you have a few of them (even 1!). To avoid filling a huge queue with drift detections and block Terraform cloud usage... you can use the limit pattern:\n\nLimit per execution and schedule multiple tfe-drift runs over the day. The result would be the same as without limiting except blocking your Terraform cloud workers for hours: At the end of the day, all your workspaces have been checked.\n\n### How does tfe-drift schedule drift detections?\n\nUsing a combination of different strategies:\n\n- Don't run already running/queued drift detection runs.\n- Don't run the workspaces where the drift detections has been executed in the last T time (e.g: 12h).\n- Prioritizing the workspaces with oldest drift detections or without previous ones.\n\n### Single run VS controller modes\n\n#### Single run\n\nSingle run is perfect for CI and crons, if you don't have a runtime to run it or you have a simple use case (e.g: few workspaces), this is the mode for you, however, take into account that you will depend on a second system that will schedule the drift-detection runs. Also if you pay for CI minutes, this may be more expensive.\n\n- Pros:\n  - Doesn't require a runtime, CI can be used.\n- Cons:\n  - Pricing (may be expensive).\n  - Lack of small time scheduling (Depends on the CI/cron system).\n\n#### Controller\n\nIf you have a cluster or a place that you can deploy long-running apps without hassle, the controller mode could be better, as you can schedule faster and smaller drift-detections (every 5m, limit of 1).\n\n- Pros:\n  - Smaller drift-detection intervals.\n  - Out of the box Prometheus metrics.\n- Cons:\n  - Requires a runtime.\n\n#### Mix\n\nRegarding the metrics, although the controller mode is the one that runs a prometheus exporter to get information for the drift detections, this can be run without the drift-detector by using `--disable-drift-detector`. So someone could run the drift-detections with the CI using single runs, and then setup the controller mode to run as a only metrics-exporter.\n\nThis use case is not very common, although may be useful in some cases.\n\n### Can be used with CI and crons (e.g: github action)?\n\nYou should! :) You even have a ready to use [github action][tfe-drift-gh-actions].\n\nIt has been designed with that in mind:\n\nHaving a cron job that executes tfe-drift regularly (e.g 1h) with a limited number of plans at the same time.\n\nAt the end of the day, all your workspaces should have been checked. tfe-drift will handle the scheduling logic so it's safe to be run at regular intervals.\n\nIt will return a json output summary so you can use it to notify or extend with other CI steps or actions.\n\n### Exit codes?\n\n- `0`: If everything is as it should.\n- `1`: If there was an error executing tfe-drift.\n- `2`: If there was any drift.\n- `3`: If there was any error on a drift detection plan.\n\nOptionally you can disable 2 and 3 exit codes in case you want to handle the drif/detection errors with the JSON summary by pipelining other applications or scripting.\n\n### Output formats?\n\nBy default, only the logger information will be written, however you can use `-o` to select an output format, the ones available are:\n\n- `json`: Non-indenten JSON.\n- `pretty-json`: Indented JSON.\n\n### Result JSON format?\n\nLook at this example:\n\n```json\n{\n        \"workspaces\": {\n                \"wk1\": {\n                        \"name\": \"wk1\",\n                        \"id\": \"ws-RAB2YhfV7mpXUTW1\",\n                        \"tags\": [\n                                \"t1\"\n                        ],\n                        \"drift_detection_run_id\": \"run-BQHxAamo7pSi1iMf\",\n                        \"drift_detection_run_url\": \"https://app.terraform.io/app/user1/workspaces/wk1/runs/run-BQHxAamo7pSi1iMf\",\n                        \"drift\": false,\n                        \"drift_detection_plan_error\": false,\n                        \"ok\": true\n                },\n                \"wk2\": {\n                        \"name\": \"wk2\",\n                        \"id\": \"ws-vz46xzDKYWpfa5o8\",\n                        \"tags\": [\n                                \"t1\",\n                                \"t4\"\n                        ],\n                        \"drift_detection_run_id\": \"run-8AgmNBY2MfKeyjGt\",\n                        \"drift_detection_run_url\": \"https://app.terraform.io/app/user1/workspaces/wk2/runs/run-8AgmNBY2MfKeyjGt\",\n                        \"drift\": false,\n                        \"drift_detection_plan_error\": false,\n                        \"ok\": true\n                },\n                \"wk3\": {\n                        \"name\": \"wk3\",\n                        \"id\": \"ws-qaCmR6EL8fujrxxY\",\n                        \"tags\": [\n                                \"t2\",\n                                \"t3\"\n                        ],\n                        \"drift_detection_run_id\": \"run-ndhST1LXMh7tn3L5\",\n                        \"drift_detection_run_url\": \"https://app.terraform.io/app/user1/workspaces/wk3/runs/run-ndhST1LXMh7tn3L5\",\n                        \"drift\": true,\n                        \"drift_detection_plan_error\": false,\n                        \"ok\": false\n                },\n                \"wk4\": {\n                        \"name\": \"wk4\",\n                        \"id\": \"ws-df3PN4CX3grHghE9\",\n                        \"tags\": [\n                                \"t1\",\n                                \"t3\"\n                        ],\n                        \"drift_detection_run_id\": \"run-Nwy7911XPX4qcwWu\",\n                        \"drift_detection_run_url\": \"https://app.terraform.io/app/user1/workspaces/wk4/runs/run-Nwy7911XPX4qcwWu\",\n                        \"drift\": false,\n                        \"drift_detection_plan_error\": false,\n                        \"ok\": true\n                }\n        },\n        \"drift\": true,\n        \"drift_detection_plan_error\": false,\n        \"ok\": false,\n        \"created_at\": \"2022-11-14T17:59:55.946884748Z\"\n}\n```\n\n[drift-detection]: https://www.hashicorp.com/campaign/drift-detection-for-terraform-cloud\n[tfe-drift-gh-actions]: https://github.com/marketplace/actions/terraform-cloud-enterprise-drift-detection\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslok%2Ftfe-drift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslok%2Ftfe-drift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslok%2Ftfe-drift/lists"}