Taint, Replace and Drift Detection
Scenario
An EC2 instance is misbehaving and you want to force Terraform to destroy and recreate it on the next apply. You also suspect someone changed an S3 bucket's tags via the console — you want to detect this drift without making any changes.
- Use terraform apply -replace=RESOURCE (modern approach) to force-replace a resource.
- Understand the deprecated terraform taint / untaint commands.
- Use terraform plan -refresh-only to detect drift.
- Use terraform apply -refresh-only to update state to match reality without changing infrastructure.
Additional Context
terraform taint was deprecated in Terraform 0.15.2. The replacement is terraform apply -replace=RESOURCE_ADDRESS. Both achieve the same result: marking a resource for destruction and recreation.
Drift detection: -refresh-only tells Terraform to read the current state of all resources from the cloud and compare it to the state file — without proposing any changes to bring things back in line. This shows you what changed outside Terraform.
main.tf
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = { source = "hashicorp/aws", version = "~> 5.0" }
}
}
provider "aws" { region = "us-east-1" }
data "aws_caller_identity" "current" {}
resource "aws_s3_bucket" "app" {
bucket = "drift-demo-${data.aws_caller_identity.current.account_id}"
tags = { Environment = "dev", ManagedBy = "terraform" }
}Workflow Commands
terraform init && terraform apply
# ─── FORCE REPLACE (modern approach) ───
terraform apply -replace="aws_s3_bucket.app"
# Terraform shows: aws_s3_bucket.app will be replaced (forces replacement)
# The bucket is destroyed and recreated.
# ─── TAINT / UNTAINT (deprecated, but on the exam) ───
terraform taint aws_s3_bucket.app # marks for replacement
terraform plan # shows destroy+create
terraform untaint aws_s3_bucket.app # removes the taint marker
# ─── DRIFT DETECTION ───
# Step 1: Change tags manually via AWS CLI
aws s3api put-bucket-tagging \
--bucket "drift-demo-123456789012" \
--tagging 'TagSet=[{Key=Environment,Value=prod},{Key=ManagedBy,Value=manual}]'
# Step 2: Detect drift with refresh-only
terraform plan -refresh-only
# Shows: aws_s3_bucket.app has been changed outside of Terraform
# ~ tags = { "Environment" = "dev" → "prod", "ManagedBy" = "terraform" → "manual" }
# Step 3: Accept the drift (update state to match reality)
terraform apply -refresh-only
# Step 4: Or, revert the drift (restore Terraform's desired state)
terraform apply # Terraform changes tags back to what's in config
terraform destroyNote: Objects have changed outside of Terraform
Terraform detected the following changes since last applying:
# aws_s3_bucket.app has been changed
~ resource "aws_s3_bucket" "app" {
~ tags = {
~ "Environment" = "dev" -> "prod"
~ "ManagedBy" = "terraform" -> "manual"
}
}
This is a refresh-only plan, so Terraform will not take any actions.
