Crisp answer: State drift is when real infrastructure differs from what
Terraform's state file records — usually because someone made a manual change
in the console. You detect it with terraform plan and reconcile it either
by importing the change into state or by reverting it with apply.
How drift happens:
- Manual change in AWS console (adding a tag, changing an instance type)
- Another tool modified the resource (CloudFormation, CDK, AWS CLI)
- A previous Terraform apply partially completed and left state inconsistent
- A resource was deleted outside Terraform
Detecting drift:
terraform plan
# If someone manually added a tag to an EC2 instance:
# ~ resource "aws_instance" "web" {
# tags = {
# + "ManualTag" = "AddedByPete" # in reality but not in config
# }
# }
# Plan: 0 to add, 1 to change, 0 to destroy.
The plan shows the diff between the desired config and what Terraform last recorded in state. But Terraform first refreshes state from AWS before comparing — so it picks up the manual change.
terraform apply -refresh-only:
Accept the drift into state without applying your config changes:
terraform apply -refresh-only
# Updates state to match reality — config is not applied
# Use when: someone made a legitimate manual change you want to keep
Reverting drift:
If you want to discard the manual change and restore Terraform's desired config:
terraform apply
# Terraform sees the drift and will override it with your config values
Removing a resource from state (without deleting it):
If someone deleted a resource outside Terraform and you don't want Terraform to recreate it:
terraform state rm aws_instance.old
# Removes from state — Terraform forgets about it, resource stays deleted
Moving resources in state:
After a refactor that renames a resource in config:
# Old config: resource "aws_instance" "web_server"
# New config: resource "aws_instance" "api"
terraform state mv aws_instance.web_server aws_instance.api
# Renames in state — prevents destroy + recreate
The moved block (Terraform 1.1+):
# Declare the rename in config so anyone running plan sees it:
moved {
from = aws_instance.web_server
to = aws_instance.api
}
What to say in the interview:
"State drift happens when someone makes a manual change outside Terraform. Running terraform plan detects it because Terraform refreshes state from AWS first. If the manual change should be kept, I use apply -refresh-only to absorb it into state. If it should be reverted, a normal apply overrides it. For resources that were deleted outside Terraform, I use terraform state rm to remove them from state so Terraform stops trying to manage them. terraform state mv is useful after renaming resources in config to avoid a destroy-and-recreate cycle."