Why targeted operations exist
By default Terraform works with the entire project at once: one plan shows all changes, one apply applies them. Sometimes you need something specific:
- Recreate one broken resource without touching the rest.
- Apply only part of the configuration to avoid pulling in unrelated changes.
- Work around a temporary bug in the provider on one resource.
For these cases there are -replace and -target.
-replace=<address>: recreate a resource
Forces Terraform to add a "destroy + create" action to the plan even when the code has not changed. Useful when a cloud resource is broken (a damaged EC2 instance, a stuck cache) but the HCL is correct.
terraform apply -replace=aws_instance.web
The plan output:
# aws_instance.web will be replaced, as requested
-/+ resource "aws_instance" "web" {...
}
Plan: 1 to add, 0 to change, 1 to destroy.
This replaces the deprecated terraform taint. Before TF 0.15.2
you had to run terraform taint aws_instance.web; terraform apply,
two steps and a manual state modification. Now it is one flag.
Addressing for -replace
Full resource addressing is supported:
# plain resource
terraform apply -replace=aws_s3_bucket.demo
# count element
terraform apply -replace='aws_instance.web[0]'
# for_each element
terraform apply -replace='aws_s3_bucket.regional["eu"]'
# resource inside a module
terraform apply -replace='module.networking.aws_vpc.main'
Note the shell quotes around addresses that contain [, ], or ".
Without quotes you get a shell error.
Multiple -replace flags
You can specify the flag more than once; all listed resources are recreated:
terraform apply \
-replace=aws_instance.web \
-replace=aws_db_instance.main
-target=<address>: apply only the specified resource
Ignores everything except the named resource (and its dependencies):
terraform apply -target=aws_s3_bucket.logs
Plan output:
Warning: Resource targeting is in effect
You are creating a plan with the -target option, which means that
the result of this plan may not represent all of the changes
requested by the current configuration.
# aws_s3_bucket.logs will be created
+ resource "aws_s3_bucket" "logs" {...
}
Terraform warns you itself that this is a bad idea. It is not an everyday tool; it is an emergency one.
When -target is justified
- You urgently need to create or change one resource while others are breaking the plan.
- You are testing one module in a large project.
- You are bootstrapping a new environment in stages (VPC first, then everything else).
- You are resolving a circular dependency by temporarily removing one side.
When -target is a bad idea
- Regular work. If you use it every day, something is wrong with the project structure. Split the configuration into separate state files instead.
- In CI/CD. No automation should depend on
-target. - To "work around" drift rather than fixing it.
- In production without an explicit incident flag. Any
-targetin prod is a deviation from normal; it belongs in a runbook.
Why -target is problematic (HashiCorp's view)
From the Terraform docs:
Targeting is a powerful but disruptive feature, and so should be used only in exceptional circumstances.
Specifically:
-
It creates a partially consistent state. After
-target, the state and the cloud reality differ from what HCL describes. That gap can persist for hours if you forget to run a full apply later. -
It hides real dependencies. If resource A implicitly depends on B through a variable or data source,
-target=Aeither fails or pulls in B unexpectedly. -
It complicates debugging. "Why is the ALB not working?" Because six weeks ago someone ran
-target=alband never applied the security_group updates.
That is why HashiCorp intentionally keeps the warning. It reminds you every time that you are doing something non-standard.
-target and dependencies
When a resource references others, those resources are also affected:
resource "aws_vpc" "main" { ... }resource "aws_subnet" "private" {vpc_id = aws_vpc.main.id
}
terraform apply -target=aws_subnet.private
This applies both the subnet and the VPC (because the subnet depends
on the VPC). That makes -target slightly less dangerous: you cannot
reference something that does not exist yet.
What these flags are not
-replaceis not a manual destroy + create. It is an atomic operation in a single apply, with all dependencies handled.-targetis not a workspace. A workspace is a separate state using the same HCL. A target is a slice of one state.-targetis not a module boundary. Targetingmodule.networkingcovers the whole module, not just "networking isolation".
Common pitfalls
-
-targetdoes not accept comma-separated values: use-target=A -target=B, not-target=A,B. Repeat the flag for each resource. -
-replacedoes not work on data blocks. Data sources are read-only; there is nothing to recreate. To "re-read" a data source, the next plan/apply will refresh it automatically. -
After
-target, always run a full apply. Otherwise you will forget and the divergence between state and HCL stays permanently. A good habit isapply -target=X && applyin one script. -
-replacemarks the action only in the plan. The state itself does not change until apply. If the plan looks wrong, simply do not apply; nothing is broken. -
-targetdoes not work withimportblocks. The declarative import introduced in TF 1.5+ applies to the entire configuration.