Skip to main content
In this guide you’ll learn how to detect and handle state drift with the Terraform CLI. We’ll cover:
  • How Terraform discovers differences between your configuration, state, and real infrastructure.
  • How to either revert remote changes or accept them into Terraform state using a refresh-only apply.
  • How to update your configuration so configuration, state, and real infrastructure remain in sync.
This demo uses a minimal AWS example: a VPC, two subnets (public and private), and an EC2 instance acting as a web server.

Terraform configuration used in this demo

main.tf
variables.tf

Deploy the configuration

Initialize and apply:
Example apply output (shortened):
Open the AWS EC2 console to verify the instance. You should see the instance tags (Name = web-server, Environment = development) and the instance type set by the instance_type variable (t2.small). I stopped the instance in the console and changed its instance type from t2.small to t3.small. Here is the “Change instance type” page showing that change:
A screenshot of the AWS EC2 console on the "Change instance type" page, showing an instance (i-0c4192b0347156b5c) with current type t2.small and a dropdown to change it to t3.small. The lower part of the page shows an instance type comparison table with pricing and specs.
After changing the instance type while stopped, I optionally updated the tags by adding a Team tag (Team = dev-app-001). The stopped instance with a successful “instance type changed” banner is shown below:
Screenshot of the AWS EC2 Instances console showing a single instance named "web-server" in the Stopped state with instance type t2.small. A green banner at the top reads "Instance type changed successfully."
I then saved tags via the console and added the Team tag:
A screenshot of the AWS EC2 "Manage tags" page showing tag entries: Name = web-server, Environment = development, and Team = dev-app-001, with Remove buttons and Save/Cancel actions. The browser window and AWS console header are visible at the top.
At this point, two manual changes were made outside of Terraform:
  • Instance type: t2.small → t3.small
  • Added tag: Team = dev-app-001

Detecting drift with Terraform

Terraform refreshes the state by default during terraform plan, so running a plan will reveal drift:
Example relevant output after the refresh step (shortened):
This output shows exactly what Terraform detected as different between the configuration & state and the real resource.

Options after detecting drift

You have two primary choices after detecting drift:

Revert remote changes (make Terraform the source of truth)

If you want to overwrite the remote changes and enforce your Terraform configuration, run a normal apply:
Terraform will plan an in-place update to change the instance type back to t2.small and remove the Team tag (since the configuration doesn’t include that tag). Example plan summary:
Approve to apply the changes and restore the resource to the declared state.

Accept remote changes into Terraform state (refresh-only apply)

If the manual changes are intentional and you want Terraform to record them without modifying infrastructure, run a refresh-only apply:
A refresh-only apply updates the Terraform state to match current real infrastructure but does not modify remote resources. Use this when external changes are desired and you want Terraform to record them in the state.
When you run terraform apply -refresh-only, Terraform shows a refresh-only plan and prompts:
Confirming writes the updated values (for example, instance_type = "t3.small" and the new Team tag) into the state file while leaving the EC2 instance unchanged.

After accepting remote changes into state

  • The Terraform state now reflects the actual remote values (e.g., "instance_type": "t3.small" and the Team tag).
  • Your configuration files still define instance_type = "t2.small" and do not include the Team tag. If you later run a normal terraform apply, Terraform will detect that the configuration differs from the state and will plan to change real resources to match the configuration.
If you want to keep the manual changes going forward, update your Terraform configuration so configuration, state, and real infrastructure match.

Update configuration to match the desired state

Change the variable default and resource tags in your configuration to reflect the accepted remote changes. variables.tf
main.tf — update tags
Then run:
After updating both configuration and state, terraform apply should report no changes:

Quick checklist

  • Detect drift: terraform plan (or terraform apply) — Terraform refreshes state during planning.
  • Revert remote changes: terraform apply — Terraform will change the remote resources to match configuration.
  • Accept remote changes (state-only): terraform apply -refresh-only — Terraform updates the state without touching remote resources.
  • To keep accepted remote changes permanently: update your Terraform configuration and run terraform apply.

References

Watch Video