A plan that shows a destroy-and-recreate of resources you did not touch is a red flag, not a routine change. Stop and find out why before anyone runs apply.
A field that used to be updatable became immutable in a new provider major version. Your config didn't change; the replacement semantics did.
Renaming a resource makes Terraform see a destroy + create. `moved` blocks preserve the real object instead.
CI is pointed at a different state file than the one holding your real resources, so it plans to build duplicates — and the old one looks 'orphaned'.
terraform plan -no-color | grep -B3 'must be replaced'
terraform workspace list && terraform state list | head
moved {
from = aws_instance.app
to = module.app.aws_instance.app
}
terraform plan -lock=false -out=tfplan
Never apply a plan you can't explain line by line. On production, add a canary: apply with -target on one disposable resource first and watch what actually happens in the console.
Terraform only knows state: if the resources are missing from state (state loss, a botched state rm, workspace switch, or a rename without moved blocks), Terraform plans to create new ones — and the OLD plan's destroy threat comes from name-changed resources it now sees as both absent and recreatable. Always read the plan's create/destroy symmetry.
terraform plan -detailed-exitcode + a pipeline gate that fails on the destroy-containing code, and never auto-apply. Review every plan with destroy counts > 0 before apply; use workspaces/state isolation so a plan can only ever see its own state.
An opinionated VPC module: per-AZ NAT, explicit dependencies, EKS-ready outputs.
Terraform AWS Foundation — $37 →One-time. Yours to modify. Instant download from the NinjaOps template store.