diff --git a/terraform/context_apply_test.go b/terraform/context_apply_test.go index 044e6666d9e5..4ee704e4512d 100644 --- a/terraform/context_apply_test.go +++ b/terraform/context_apply_test.go @@ -8719,3 +8719,45 @@ func TestContext2Apply_multiRef(t *testing.T) { t.Fatalf("expected 1 depends_on entry for aws_instance.create, got %q", deps) } } + +func TestContext2Apply_targetedModuleRecursive(t *testing.T) { + m := testModule(t, "apply-targeted-module-recursive") + p := testProvider("aws") + p.ApplyFn = testApplyFn + p.DiffFn = testDiffFn + ctx := testContext2(t, &ContextOpts{ + Module: m, + ProviderResolver: ResourceProviderResolverFixed( + map[string]ResourceProviderFactory{ + "aws": testProviderFuncFixed(p), + }, + ), + Targets: []string{"module.child"}, + }) + + if _, err := ctx.Plan(); err != nil { + t.Fatalf("err: %s", err) + } + + state, err := ctx.Apply() + if err != nil { + t.Fatalf("err: %s", err) + } + + mod := state.ModuleByPath([]string{"root", "child", "subchild"}) + if mod == nil { + t.Fatalf("no subchild module found in the state!\n\n%#v", state) + } + if len(mod.Resources) != 1 { + t.Fatalf("expected 1 resources, got: %#v", mod.Resources) + } + + checkStateString(t, state, ` + +module.child.subchild: + aws_instance.foo: + ID = foo + num = 2 + type = aws_instance + `) +} diff --git a/terraform/graph_builder_plan.go b/terraform/graph_builder_plan.go index a6a3a90d4860..4b29bbb4b8ba 100644 --- a/terraform/graph_builder_plan.go +++ b/terraform/graph_builder_plan.go @@ -117,7 +117,15 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { &CountBoundaryTransformer{}, // Target - &TargetsTransformer{Targets: b.Targets}, + &TargetsTransformer{ + Targets: b.Targets, + + // Resource nodes from config have not yet been expanded for + // "count", so we must apply targeting without indices. Exact + // targeting will be dealt with later when these resources + // DynamicExpand. + IgnoreIndices: true, + }, // Close opened plugin connections &CloseProviderTransformer{}, diff --git a/terraform/graph_builder_refresh.go b/terraform/graph_builder_refresh.go index 0634f9698d8f..3d3e968fae9e 100644 --- a/terraform/graph_builder_refresh.go +++ b/terraform/graph_builder_refresh.go @@ -144,7 +144,15 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { &ReferenceTransformer{}, // Target - &TargetsTransformer{Targets: b.Targets}, + &TargetsTransformer{ + Targets: b.Targets, + + // Resource nodes from config have not yet been expanded for + // "count", so we must apply targeting without indices. Exact + // targeting will be dealt with later when these resources + // DynamicExpand. + IgnoreIndices: true, + }, // Close opened plugin connections &CloseProviderTransformer{}, diff --git a/terraform/test-fixtures/apply-targeted-module-recursive/child/main.tf b/terraform/test-fixtures/apply-targeted-module-recursive/child/main.tf new file mode 100644 index 000000000000..852bce8b9f39 --- /dev/null +++ b/terraform/test-fixtures/apply-targeted-module-recursive/child/main.tf @@ -0,0 +1,3 @@ +module "subchild" { + source = "./subchild" +} diff --git a/terraform/test-fixtures/apply-targeted-module-recursive/child/subchild/main.tf b/terraform/test-fixtures/apply-targeted-module-recursive/child/subchild/main.tf new file mode 100644 index 000000000000..98f5ee87e9f0 --- /dev/null +++ b/terraform/test-fixtures/apply-targeted-module-recursive/child/subchild/main.tf @@ -0,0 +1,3 @@ +resource "aws_instance" "foo" { + num = "2" +} diff --git a/terraform/test-fixtures/apply-targeted-module-recursive/main.tf b/terraform/test-fixtures/apply-targeted-module-recursive/main.tf new file mode 100644 index 000000000000..0f6991c536ca --- /dev/null +++ b/terraform/test-fixtures/apply-targeted-module-recursive/main.tf @@ -0,0 +1,3 @@ +module "child" { + source = "./child" +} diff --git a/terraform/transform_targets.go b/terraform/transform_targets.go index 125f9e302155..4f117b4f732b 100644 --- a/terraform/transform_targets.go +++ b/terraform/transform_targets.go @@ -41,6 +41,12 @@ type TargetsTransformer struct { // that already have the targets parsed ParsedTargets []ResourceAddress + // If set, the index portions of resource addresses will be ignored + // for comparison. This is used when transforming a graph where + // counted resources have not yet been expanded, since otherwise + // the unexpanded nodes (which never have indices) would not match. + IgnoreIndices bool + // Set to true when we're in a `terraform destroy` or a // `terraform plan -destroy` Destroy bool @@ -199,7 +205,12 @@ func (t *TargetsTransformer) nodeIsTarget( addr := r.ResourceAddr() for _, targetAddr := range addrs { - if targetAddr.Equals(addr) { + if t.IgnoreIndices { + // targetAddr is not a pointer, so we can safely mutate it without + // interfering with references elsewhere. + targetAddr.Index = -1 + } + if targetAddr.Contains(addr) { return true } } diff --git a/website/docs/commands/apply.html.markdown b/website/docs/commands/apply.html.markdown index 3c00e7c0597f..55c26c7ad046 100644 --- a/website/docs/commands/apply.html.markdown +++ b/website/docs/commands/apply.html.markdown @@ -54,9 +54,9 @@ The command-line flags are all optional. The list of available flags are: [remote state](/docs/state/remote.html) is used. * `-target=resource` - A [Resource - Address](/docs/internals/resource-addressing.html) to target. Operation will - be limited to this resource and its dependencies. This flag can be used - multiple times. + Address](/docs/internals/resource-addressing.html) to target. For more + information, see + [the targeting docs from `terraform plan`](/docs/commands/plan.html#resource-targeting). * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. Variable values are interpreted as diff --git a/website/docs/commands/plan.html.markdown b/website/docs/commands/plan.html.markdown index 5d4c910392ff..de4f915949fe 100644 --- a/website/docs/commands/plan.html.markdown +++ b/website/docs/commands/plan.html.markdown @@ -63,9 +63,8 @@ The command-line flags are all optional. The list of available flags are: Ignored when [remote state](/docs/state/remote.html) is used. * `-target=resource` - A [Resource - Address](/docs/internals/resource-addressing.html) to target. Operation will - be limited to this resource and its dependencies. This flag can be used - multiple times. + Address](/docs/internals/resource-addressing.html) to target. This flag can + be used multiple times. See below for more information. * `-var 'foo=bar'` - Set a variable in the Terraform configuration. This flag can be set multiple times. Variable values are interpreted as @@ -78,6 +77,37 @@ The command-line flags are all optional. The list of available flags are: files specified by `-var-file` override any values in a "terraform.tfvars". This flag can be used multiple times. +## Resource Targeting + +The `-target` option can be used to focus Terraform's attention on only a +subset of resources. +[Resource Address](/docs/internals/resource-addressing.html) syntax is used +to specify the constraint. The resource address is interpreted as follows: + +* If the given address has a _resource spec_, only the specified resource + is targeted. If the named resource uses `count` and no explicit index + is specified in the address, all of the instances sharing the given + resource name are targeted. + +* The the given address _does not_ have a resource spec, and instead just + specifies a module path, the target applies to all resources in the + specified module _and_ all of the descendent modules of the specified + module. + +This targeting capability is provided for exceptional circumstances, such +as recovering from mistakes or working around Terraform limitations. It +is *not recommended* to use `-target` for routine operations, since this can +lead to undetected configuration drift and confusion about how the true state +of resources relates to configuration. + +Instead of using `-target` as a means to operate on isolated portions of very +large configurations, prefer instead to break large configurations into +several smaller configurations that can each be independently applied. +[Data sources](/docs/configuration/data-sources.html) can be used to access +information about resources created in other configurations, allowing +a complex system architecture to be broken down into more managable parts +that can be updated independently. + ## Security Warning Saved plan files (with the `-out` flag) encode the configuration,