Skip to content

Drift, Refresh and Reconciliation


Terraform operates across three related but distinct representations of infrastructure:

Terraform Configuration
        │
        │ desired configuration
        ▼
Terraform State
        │
        │ recorded understanding
        ▼
Real Infrastructure

These are connected, but they are not identical. Production operations become much easier to reason about once you understand how they can diverge.

What Is Infrastructure Drift?

Drift occurs when real infrastructure changes outside the Terraform workflow and Terraform’s recorded understanding no longer matches reality.

For example:

Terraform configuration
instance_type = "t3.micro"

Terraform state
instance_type = "t3.micro"

Real infrastructure
instance_type = "t3.large"

Someone may have changed the instance type manually in a cloud console.

Other sources of drift include:

  • another automation system changing a resource
  • a resource being deleted manually
  • a cloud service changing something automatically
  • an administrator modifying configuration
  • a separate Terraform project managing the same object
  • external systems modifying attributes Terraform also manages

Drift is therefore not necessarily a Terraform bug. It is a consequence of multiple systems being capable of changing the same infrastructure.

Configuration, State, And Reality

It helps to separate four questions.

What Do I Want?

Terraform configuration expresses desired infrastructure.

resource "terraform_data" "example" {
  input = "desired"
}

What Does Terraform Remember?

Terraform state records Terraform’s understanding of managed resources.

What Exists Right Now?

The provider can query the actual infrastructure.

What Should Terraform Do About The Difference?

Terraform compares information and produces a plan.

That gives us:

Desired configuration
        │
        ├───────────────┐
        │               │
        ▼               ▼
 Terraform state    Real infrastructure
        │               │
        └────── compare ┘
                │
                ▼
              Plan

Detecting Drift With terraform plan

One of the simplest ways to discover drift is:

terraform plan

Terraform refreshes its understanding of managed resources as part of normal planning behavior and compares the resulting information with configuration.

Suppose your configuration says:

resource "terraform_data" "example" {
  input = "original"
}

After applying it, someone changes the external object.

When you run:

terraform plan

Terraform can detect that the current infrastructure differs from what the configuration describes and propose a reconciliation.

The exact plan depends on the resource and provider, but conceptually:

Configuration: original
State before refresh: original
Real infrastructure: changed
             ↓
        Terraform refreshes
             ↓
State understanding: changed
             ↓
Configuration differs
             ↓
Plan proposes correction

This is why drift detection is not simply “compare the state file with the cloud.” Terraform needs current provider information to understand what exists now.

Refresh Terminology Has Changed

Older Terraform tutorials often teach a separate workflow:

terraform refresh

That is outdated as the primary way to think about current Terraform workflows.

Modern Terraform performs refresh-related behavior as part of planning and applying, and provides explicit refresh-only operations when you want to update state without applying configuration changes.

The important current commands are:

terraform plan -refresh-only

and:

terraform apply -refresh-only

The first lets you review what updating Terraform’s state from remote infrastructure would change.

The second applies that refresh-only operation.

Note

Be careful with older tutorials that describe terraform refresh as the normal standalone workflow. Current Terraform encourages you to think in terms of normal plan/apply refresh behavior and explicit -refresh-only operations when you intentionally want a refresh-only workflow.

What Does terraform plan -refresh-only Do?

A refresh-only plan asks Terraform to update its understanding of remote objects without applying the configuration’s desired changes.

Run:

terraform plan -refresh-only

Conceptually:

Real infrastructure
        ↓
Provider reads current values
        ↓
Terraform calculates state changes
        ↓
You review the refresh-only plan

This is useful when you want to understand what changed in the remote infrastructure and how Terraform’s state would be updated.

You can then apply that state refresh deliberately:

terraform apply -refresh-only

The distinction matters because refreshing state is not the same as reconciling infrastructure to configuration.

Four Different Operations

These four concepts are easy to confuse.

Detecting Drift

You discover that infrastructure differs from the expected state.

A normal plan is often the most useful mechanism.

terraform plan

Refreshing State

You update Terraform’s recorded information to reflect current remote infrastructure without intentionally applying configuration changes.

terraform plan -refresh-only
terraform apply -refresh-only

Reconciling Infrastructure

You decide that Terraform configuration is authoritative and want real infrastructure brought back into compliance.

terraform plan
terraform apply

Intentionally Accepting The External Change

You decide that the external change is actually the new desired state.

In that case, update the Terraform configuration and then plan:

External change
      ↓
Review
      ↓
Decide it is intentional
      ↓
Update Terraform configuration
      ↓
terraform plan

Do not simply refresh state and call the problem solved. If the configuration still expresses the old desired state, the next normal plan may propose changing the infrastructure again.

A Small Drift Example

Create:

terraform-drift-example/
└── main.tf

Use:

terraform {
  required_version = ">= 1.6.0"
}

resource "terraform_data" "example" {
  input = "original"
}

Initialize and apply:

terraform init
terraform apply

Now imagine an external system changes the managed object’s effective value. With a real provider resource, this could happen through a cloud console or another automation system.

The workflow becomes:

Terraform configuration
        │
        │ "original"
        ▼
Real infrastructure
        │
        │ external change
        ▼
Real infrastructure
        │
        │ now differs
        ▼
terraform plan
        │
        ▼
Terraform proposes reconciliation

The example is intentionally provider-independent. The production lesson is more important than a contrived cloud resource: external changes create an ownership question.

Reverting Infrastructure To Configuration

Suppose the external change was accidental.

Then Terraform configuration remains authoritative:

External change
      ↓
terraform plan
      ↓
Unexpected difference identified
      ↓
Review
      ↓
terraform apply
      ↓
Infrastructure returns to desired configuration

This is reconciliation.

The important safety step is reviewing the plan before applying it. Drift can involve destructive or expensive changes.

Warning

Do not blindly apply a drift-reconciliation plan. The external change may have been intentional, and Terraform may propose a replacement or another disruptive operation.

Updating Configuration To Match Reality

Sometimes the external change is intentional.

For example:

Old Terraform configuration
instance size = small

External change
instance size = medium

Decision
medium is now the intended production configuration

In that case, update the Terraform configuration to medium.

Do not merely refresh state and leave configuration saying small.

The desired workflow is:

Real change
    ↓
Review and approve intent
    ↓
Update Terraform configuration
    ↓
Plan
    ↓
No unexpected reconciliation

That preserves Terraform as the source of declared intent.

Drift Detection In CI/CD

Drift can remain invisible if Terraform is only executed when someone submits a code change.

A production team can instead schedule drift detection.

Conceptually:

Scheduled CI job
       ↓
terraform plan
       ↓
Unexpected changes?
     /       \
   no         yes
   │           │
done       alert/review

The CI job does not necessarily need to apply anything. Detection and remediation can remain separate.

This is often safer:

Detect
  ↓
Notify
  ↓
Investigate
  ↓
Decide
  ↓
Reconcile intentionally

Rather than:

Detect
  ↓
Automatically overwrite everything

The right policy depends on the ownership model and risk of the infrastructure.

When Drift Is A Design Problem

Repeated drift is often a sign that ownership is unclear.

Imagine:

Terraform ──────┐
                ├── same resource
Admin console ──┤
                │
Automation ─────┘

If all three systems are allowed to modify the same attributes, Terraform will continually fight the other systems.

Possible solutions include:

  • establish Terraform as the owner
  • move the external change into Terraform
  • divide ownership by attribute or resource where the provider supports it safely
  • change the architecture so only one system manages a resource
  • intentionally use lifecycle behavior where appropriate

The worst solution is often simply hiding every difference.

Drift And Operational Ownership

A healthy production process asks:

Who owns this resource?
Who is allowed to change it?
How are changes reviewed?
How are external changes detected?
What happens when drift is found?

Terraform is most predictable when ownership is explicit.

The operational mental model is:

Configuration
     │
     │ desired state
     ▼
Terraform
     │
     ├── reads real infrastructure
     │
     ├── compares state
     │
     └── produces a plan
             │
             ▼
        human decision
          /       \
 reconcile       accept change
    │                │
 apply          update configuration

Drift management is therefore not just a command. It is an operating model for deciding which system is authoritative.

Last updated on