Terraform Drift Detection and Drift Management: Find, Triage, and Fix Drift
Terraform drift can lead to security vulnerabilities and operational chaos. Understand what causes drift, how to detect it, how to manage it from triage to resolution, and how Kestra runs automated checks to keep your infrastructure in sync.
TL;DR — Terraform drift is any difference between your Terraform configuration and the resources actually running, usually caused by manual or out-of-band changes.
terraform plan -refresh-only -detailed-exitcodedetects it for managed resources. Drift management is what happens next: triage, a decision to accept or revert, reconciliation, and prevention. Running the check on a schedule, with alerts and a recorded history, turns drift from a surprise into a routine ticket.
Infrastructure-as-Code (IaC) promises consistent, predictable environments, but the reality often involves unexpected deviations. These inconsistencies, known as Terraform drift, occur when your live infrastructure no longer matches the state defined in your Terraform configurations. Whether it’s a manual change, an emergency fix, or an update from another automated system, drift erodes the reliability and security benefits of IaC.
This guide explains what Terraform drift is, why it matters, how Terraform detects it natively, how to manage drift once it is found, and how to run drift detection automatically with Kestra.
Understanding Terraform Drift and Its Impact
At its core, drift represents a loss of control and visibility over your infrastructure. When the code is no longer the single source of truth, managing infrastructure becomes reactive and risky.
What is Terraform Drift?
Terraform drift is any discrepancy between the actual state of your infrastructure (e.g., resources in AWS, GCP, or Azure) and the state that Terraform expects based on your configuration files (.tf) and state file (.tfstate).
This divergence can happen for several reasons:
- Manual Changes: An engineer makes a quick change directly in the cloud provider’s console to resolve an incident.
- Out-of-Band Automation: Another system, like a security script or a different IaC tool, modifies a resource that Terraform manages.
- Human Error: A resource is deleted or modified accidentally outside of the standard Terraform workflow.
- Resource Decay: Cloud providers sometimes make automatic changes to resources, such as updating a managed database version, which may not be reflected in your code.
Understanding the principles of Infrastructure-as-Code explains why drift matters so much.
Why Unmanaged Drift is a Risk
Ignoring drift isn’t an option. The consequences can be severe:
- Security Vulnerabilities: Unapproved changes can create security holes, such as opening a firewall port or misconfiguring IAM permissions, exposing your systems to attack.
- Operational Instability: When the live environment is unknown, future Terraform runs can have unpredictable and potentially destructive outcomes. An
applymight overwrite a critical manual fix or fail entirely. - Compliance Gaps: In regulated industries, auditors expect changes to go through an approved process. Undetected drift makes it hard to show that the running infrastructure matches what was approved.
- Erosion of IaC Benefits: The core value of IaC (predictability, repeatability, and auditability) is lost. Your infrastructure reverts to a state of undocumented, manual configuration.
How Terraform Detects Drift Natively
Terraform provides built-in commands to identify drift, forming the foundation of any detection strategy. The process relies on comparing three sources of information: your code, the state file, and the real-world infrastructure.
How terraform plan Finds Drift
When you run terraform plan, Terraform:
- Reads the configuration: it parses your
.tffiles to build the desired state. - Reads the state: it loads the state file, which records the resources Terraform last created or modified.
- Refreshes the state: it queries the provider APIs for the current attributes of every resource in the state.
- Compares and plans: it shows the differences as a plan of resources to create, update, or destroy.
A normal plan mixes two things: changes you made in code and changes made outside Terraform. To see only drift, use terraform plan -refresh-only, which reports how the real resources differ from the state without proposing configuration changes. The standalone terraform refresh command is deprecated in favor of this option.
Add -detailed-exitcode to make the result machine-readable:
- 0: no changes; infrastructure is in sync.
- 1: an error occurred.
- 2: changes were found; infrastructure has drifted.
Distinguishing Managed vs. Unmanaged Resource Drift
Drift can be categorized into two types:
- Managed Drift: This occurs when a resource managed by Terraform is altered outside of the IaC workflow. For example, a tag is manually added to an EC2 instance.
terraform planwill detect this and propose a plan to revert the change. - Unmanaged Drift: This involves resources that Terraform doesn’t know about at all. For instance, an engineer manually creates a new S3 bucket. Because this resource isn’t in the state file, a standard
terraform planwon’t see it. Detecting it requires an inventory of the cloud account to compare against the state, for example from your provider’s configuration inventory service. driftctl did this for AWS, Azure, and GCP, but the project is now in maintenance mode.
This guide focuses on detecting managed drift, which is the most common and immediate challenge for teams using Terraform for orchestration.
Drift Management: From Detection to Resolution
Detection only tells you that something changed. Drift management is the practice of detecting, triaging, resolving, and preventing differences between the declared configuration of your infrastructure and what is actually running. Without it, drift reports pile up in a channel and nobody acts on them.
| Step | Question it answers | Typical practice |
|---|---|---|
| 1. Detect | What changed, and where? | Scheduled plan -refresh-only runs per workspace, cloud inventory scans for unmanaged resources, Ansible check mode for server configuration |
| 2. Triage | How serious is it? | Security-relevant resources first (IAM, security groups, encryption, public access), production before staging and dev, cosmetic changes such as tags last |
| 3. Decide | Keep the change or revert it? | The resource owner decides, using the rule of thumb below, and the decision is recorded with the drift report |
| 4. Reconcile | Is the code the source of truth again? | Accept: a pull request that updates the configuration or imports the resource. Revert: re-apply the code through your normal reviewed pipeline. The next check comes back clean |
| 5. Prevent | Why did it happen? | Read-only console access by default, audited break-glass accounts, policy as code, and lifecycle { ignore_changes } for attributes another system owns |
| 6. Measure | Is drift going down? | Share of workspaces drifted per week, mean time to resolve, and repeat offenders by resource type or team |
Accept or Revert: A Rule of Thumb
- Revert when the change weakens security or compliance, was made by mistake, or nobody claims it.
- Accept when it was an incident fix that should become the standard, or a resource someone created that should be managed from now on. Update the code, or use
terraform import, so the next plan is clean. - Exclude attributes that another system legitimately manages, such as an autoscaling group’s desired capacity, with
ignore_changes, so they stop showing up as drift. - Never leave drift open. An unresolved difference makes the next
applyunpredictable: it may silently undo an emergency fix or fail halfway through.
Configuration Drift Beyond Terraform
Terraform state is one place drift shows up. Configuration drift happens at every layer, and a drift management practice should cover the layers you own:
- Inside servers: packages, files, users, and services diverge from the playbook.
ansible-playbook --check --diffreports the difference without changing anything; see this Ansible configuration drift blueprint. - Unmanaged cloud resources: resources created outside any IaC never appear in a plan. Compare the cloud inventory (AWS Config, Azure Resource Graph, Google Cloud Asset Inventory) with your state files to find them.
- Kubernetes: GitOps controllers such as Argo CD or Flux detect and reconcile manifest drift continuously; cluster add-ons and settings outside Git still need their own checks.
Drift checks are recurring work, not a project: they belong with the other scheduled day 2 operations that keep infrastructure correct after go-live.
Why Terraform Drift Detection Needs Orchestration
Running terraform plan by hand works for one workspace. Across many environments, teams, and clouds, the check needs to run on its own and route its results.
An orchestration platform adds what a one-off CI job does not. HCP Terraform users also get health assessments for their workspaces; an orchestrator covers the rest of the estate:
- Scheduled, Continuous Checks: Run drift detection on a recurring schedule (e.g., daily or hourly) to catch deviations quickly, not just when a pull request is opened.
- Centralized Alerting: Automatically route notifications to the right team via Slack, Teams, or PagerDuty when drift is detected, ensuring it doesn’t go unnoticed.
- Conditional Logic: Parse exit codes and plan outputs to trigger different workflows, such as creating an incident ticket for critical drift or opening a pull request for a change the owner wants to keep.
- Cross-System Coordination: Manage drift detection across dozens of Terraform projects, each with its own state file and cloud credentials, from a single control plane.
- Auditability: Maintain a historical record of all drift checks, providing a clear audit trail for compliance and security reviews.
Ready to orchestrate your infrastructure?
Orchestrate Terraform Drift Detection with Kestra: A Daily Sentinel
The following flow runs every morning. It clones the repository that holds the Terraform code, runs a refresh-only plan, stores the plan output, and alerts Slack when drift is found.
id: terraform-drift-detectionnamespace: company.team.sre
inputs: - id: repo_url type: STRING defaults: https://github.com/your-org/infrastructure.git
triggers: - id: daily type: io.kestra.plugin.core.trigger.Schedule cron: "0 7 * * *"
tasks: - id: drift_workspace type: io.kestra.plugin.core.flow.WorkingDirectory tasks: - id: clone_repository type: io.kestra.plugin.git.Clone url: "{{ inputs.repo_url }}" branch: main
- id: terraform_plan type: io.kestra.plugin.terraform.cli.TerraformCLI containerImage: hashicorp/terraform:1.9 env: AWS_ACCESS_KEY_ID: "{{ secret('AWS_ACCESS_KEY_ID') }}" AWS_SECRET_ACCESS_KEY: "{{ secret('AWS_SECRET_ACCESS_KEY') }}" beforeCommands: - terraform init -input=false commands: - | set +e terraform plan -refresh-only -detailed-exitcode -input=false -no-color > plan.txt code=$? cat plan.txt echo "::{\"outputs\":{\"plan_exit_code\":$code}}::" [ "$code" -ne 1 ] outputFiles: - plan.txt
- id: check_for_drift type: io.kestra.plugin.core.flow.If condition: "{{ outputs.terraform_plan.vars.plan_exit_code == 2 }}" then: - id: notify_on_drift type: io.kestra.plugin.slack.notifications.SlackIncomingWebhook url: "{{ secret('SLACK_WEBHOOK_URL') }}" payload: | {"text": "Terraform drift detected in {{ inputs.repo_url }}. Plan output: execution {{ execution.id }} (plan.txt)."} else: - id: log_in_sync type: io.kestra.plugin.core.log.Log message: "No drift: infrastructure matches the Terraform state."What’s worth noticing in this workflow:
- Declarative & Version-Controlled: The entire workflow is a single YAML file that can be stored in Git, just like your Terraform code.
- Shared working directory:
WorkingDirectorylets the clone and the Terraform task use the same files, and the check always runs against the latestmain. - Exit code handling: the script captures the plan exit code and publishes it as an output. Exit code 2 (drift) does not fail the task, while exit code 1 (a real error) still does.
- Secrets: cloud credentials and the Slack webhook come from Kestra secrets, not from the flow definition.
- Extensibility: You can extend it to create a Jira ticket, trigger a remediation playbook, or promote a change to another environment after a manual approval step. You can also adapt this pattern for related tools using blueprints for OpenTofu drift detection or Terragrunt drift detection. The Terraform plugin covers the other Terraform commands.
Where Automated Drift Detection Pays Off
Automated drift detection helps in several places:
- Compliance evidence: a dated record of every check helps show auditors that changes outside the approved process are caught and handled, for frameworks such as PCI DSS, SOC 2, or HIPAA.
- Maintaining Security Posture: By immediately flagging unauthorized changes, you can close security gaps before they are exploited. Reverting a change to a security group or IAM policy then goes through the same reviewed pipeline as any other change.
- Preventing Outages: Drift in networking rules, database configurations, or auto-scaling groups can lead to performance degradation or outages. Early detection allows you to fix these issues proactively.
- GitOps: drift detection enforces the rule that Git is the source of truth for your infrastructure.
- Reducing Manual Toil: Automating these checks frees up your platform engineering team from tedious manual validation, allowing them to focus on building new capabilities. You can even use this pattern to detect drift in specific resources like a GCS bucket retention policy.
Related Concepts
- Infrastructure Automation Resources: Explore more guides and playbooks for Terraform, Ansible, and IaC.
- Puppet vs. Kestra: Compare different approaches to configuration management and infrastructure orchestration.
- Orchestrate Ansible with Kestra: Learn how to combine configuration management with workflow orchestration.
- Orchestrate Jenkins with Kestra: Integrate your CI/CD pipelines with broader infrastructure automation workflows.
Automated drift detection keeps Infrastructure as Code true after the first apply, and it is one of the recurring day 2 operations every infrastructure team runs. Kestra runs the checks on schedule, keeps their history, and connects the result to the people and systems that act on it.
Terraform, Ansible and Kubernetes, orchestrated from one place.
Related resources
Frequently asked questions
Find answers to your questions right here, and don't hesitate to Contact Us if you couldn't find what you're looking for.