Resource Targeting
Resource targeting lets you narrow a terraform plan or terraform apply to a specific subset of your configuration using the -target flag. Instead of evaluating and reconciling every resource in the working directory, Terraform restricts its attention to the addresses you name plus their dependencies. This is a powerful surgical tool — but it is explicitly an escape hatch, not part of a healthy day-to-day workflow. Understanding when it helps and how it can quietly damage your state is the difference between a clean recovery and a deeper mess.
How -target works
When you pass -target=ADDRESS, Terraform builds a reduced dependency graph. It includes the targeted resource, everything that resource depends on (so it can compute its arguments correctly), and the providers required to manage them. Resources that are not in that subgraph are skipped entirely — they are neither refreshed, planned, nor changed.
The address you supply uses standard Terraform resource address syntax: a managed resource is type.name, a module call is module.name, and you can index into collections with [index] or ["key"].
# A single resource
terraform apply -target=aws_instance.web
# One instance from a count/for_each resource
terraform apply -target='aws_instance.web[0]'
terraform apply -target='aws_instance.web["us-east-1a"]'
# An entire module (all resources inside it)
terraform apply -target=module.networking
# A resource nested inside a module
terraform apply -target=module.networking.aws_subnet.private
You can repeat the flag to target several addresses at once. The -target flag works identically in OpenTofu, which mirrors Terraform’s CLI surface.
terraform apply -target=aws_instance.web -target=aws_eip.web
A worked example
Consider a small configuration with a security group, an instance, and an Elastic IP:
resource "aws_security_group" "web" {
name = "web-sg"
description = "Allow HTTP"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web" {
ami = "ami-0c02fb55956c7d316"
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
}
resource "aws_eip" "web" {
instance = aws_instance.web.id
domain = "vpc"
}
If you target only the instance, Terraform pulls in the security group because the instance depends on it, but it leaves the Elastic IP untouched:
terraform apply -target=aws_instance.web
Output:
aws_security_group.web: Refreshing state... [id=sg-0a1b2c3d4e5f]
aws_instance.web: Refreshing state... [id=i-09f8e7d6c5b4a]
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
Terraform will perform the following actions:
# aws_instance.web will be updated in-place
~ resource "aws_instance" "web" {
id = "i-09f8e7d6c5b4a"
~ instance_type = "t3.micro" -> "t3.small"
}
Plan: 0 to add, 1 to change, 0 to destroy.
╷
│ 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.
╵
Note the warning: Terraform always tells you the plan is partial. The Elastic IP is absent from the plan even if its configuration drifted.
Legitimate use cases
Targeting exists for situations where applying the full configuration is impossible, unsafe, or unnecessarily slow.
| Use case | Why -target helps |
|---|---|
| Recovering from a failed apply | A partial apply left some resources created and others not. Targeting the stuck resource lets you finish without re-planning everything. |
| Working around a provider bug | A dependency cycle or buggy refresh blocks the full plan; targeting routes around the broken resource until a fix lands. |
| Breaking a chicken-and-egg bootstrap | Create a resource that another resource’s data source needs to read before that data source can be evaluated. |
| Debugging | Isolate one resource to see exactly what Terraform proposes for it, without noise from unrelated diffs. |
| Large, slow configurations | When you are certain only one resource changed, targeting avoids a long full refresh — though this should be temporary. |
Why it is an escape hatch, not routine
The core risk is state divergence. Because non-targeted resources are skipped, your applied infrastructure can drift away from what the configuration as a whole describes. The next untargeted apply will then surface a pile of changes you forgot about — and those changes may interact in ways you did not intend.
Targeting also silently ignores dependents. If you target a resource and change an output other resources consume, those downstream resources are not updated until a later full apply, leaving a window of inconsistency.
Warning: Routine use of
-targetis a strong signal that your configuration should be split into smaller, independently managed root modules (separate state files). If you find yourself reaching for-targetevery day, refactor the layout rather than working around it.
Tip: After any targeted apply, run a plain
terraform plan(no-target) as soon as it is safe. A clean “No changes” result confirms your real infrastructure matches the full configuration again.
For replacing a single resource — the most common reason people reach for targeting — prefer the dedicated -replace flag, which is purpose-built and does not bypass the rest of the plan:
terraform apply -replace=aws_instance.web
Best Practices
- Treat
-targetas an exception you can justify in a sentence, not a default workflow. - Always run a full, untargeted
terraform planafterward to confirm the configuration and real state have converged. - Prefer
-replaceover-targetwhen your actual goal is to recreate one resource. - If you target a module, remember every resource inside it (and its dependencies) is included — scope to the narrowest address that solves the problem.
- Quote addresses containing
[,], or"so your shell does not mangle them. - When targeting becomes habitual, split the configuration into smaller root modules with their own state instead.
- Capture the warning banner in code review or CI logs so partial applies are never invisible to your team.