Terraform Tutorial — State Drift and Conflicts
State drift occurs when someone changes infrastructure outside Terraform (console, CLI, another tool). The state file becomes stale.
Detecting Drift
Run terraform plan — it compares state with reality:
$ terraform plan
# aws_instance.web has been changed
~ resource "aws_instance" "web" {
~ instance_type = "t3.micro" -> "t3.small" # changed outside TF!
}
Plan: 0 to add, 1 to change, 0 to destroy.
Terraform wants to revert the manual change to match your config.
Option 1: Revert the Drift
If your config is correct and the manual change was a mistake:
$ terraform apply
# Reverts instance_type back to t3.micro
Option 2: Accept the Drift
If you want to keep the manual change, update your config:
resource "aws_instance" "web" {
instance_type = "t3.small" # match the manual change
}
Then verify:
$ terraform plan
No changes. Your infrastructure matches the configuration.
Option 3: Refresh State
Update state to match reality without changing config:
$ terraform refresh
Or with plan:
$ terraform plan -refresh-only
⚠️ Warning:
terraform refreshupdates state but may cause drift between your config and state. Always runplanafter refresh.
State Conflict: Resource Deleted Outside Terraform
Error: error reading S3 Bucket: NoSuchBucket: The specified bucket does not exist
Cause: Someone deleted the bucket manually but it’s still in state.
Fix:
# Remove from state (Terraform forgets it)
$ terraform state rm aws_s3_bucket.main
# Then apply to recreate it
$ terraform apply
Preventing Drift
| Strategy | How |
|---|---|
| Restrict console access | Use read-only roles for developers |
| Detect drift automatically | Run terraform plan in CI/CD on a schedule |
Use lifecycle.ignore_changes |
For attributes managed externally |
resource "aws_instance" "web" {
lifecycle {
ignore_changes = [tags] # don't revert tag changes
}
}
What’s Next?
Let’s handle the case where a resource already exists when Terraform tries to create it.