diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index 465695c96..c8f6e3fd4 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -5948,6 +5948,61 @@ resource "aws_instance" "foo" { } } +func TestContext2Plan_targetResourceInModuleInstance(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +module "mod" { + count = 3 + source = "./mod" +} +`, + "mod/main.tf": ` +resource "aws_instance" "foo" { +} +`, + }) + + p := testProvider("aws") + p.DiffFn = testDiffFn + + target, diags := addrs.ParseTargetStr("module.mod[1].aws_instance.foo") + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + targets := []addrs.Targetable{target.Subject} + + ctx := testContext2(t, &ContextOpts{ + Config: m, + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("aws"): testProviderFuncFixed(p), + }, + Targets: targets, + }) + + plan, diags := ctx.Plan() + if diags.HasErrors() { + t.Fatal(diags.ErrWithWarnings()) + } + + expected := map[string]plans.Action{ + // the single targeted mod[1] instance + `module.mod[1].aws_instance.foo`: plans.Create, + } + + for _, res := range plan.Changes.Resources { + want := expected[res.Addr.String()] + if res.Action != want { + t.Fatalf("expected %s action, got: %q %s", want, res.Addr, res.Action) + } + delete(expected, res.Addr.String()) + } + + for res, action := range expected { + t.Errorf("missing %s change for %s", action, res) + } +} + func TestContext2Plan_moduleRefIndex(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` diff --git a/terraform/graph_builder_plan.go b/terraform/graph_builder_plan.go index 1cb0e5ee2..0354ce238 100644 --- a/terraform/graph_builder_plan.go +++ b/terraform/graph_builder_plan.go @@ -157,12 +157,6 @@ func (b *PlanGraphBuilder) Steps() []GraphTransformer { // Target &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, }, // Detect when create_before_destroy must be forced on for a particular diff --git a/terraform/graph_builder_refresh.go b/terraform/graph_builder_refresh.go index 546fdff63..e91123a1c 100644 --- a/terraform/graph_builder_refresh.go +++ b/terraform/graph_builder_refresh.go @@ -176,12 +176,6 @@ func (b *RefreshGraphBuilder) Steps() []GraphTransformer { // Target &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 diff --git a/terraform/transform_targets.go b/terraform/transform_targets.go index e2b2cf525..abbc92d4f 100644 --- a/terraform/transform_targets.go +++ b/terraform/transform_targets.go @@ -22,12 +22,6 @@ type GraphNodeTargetable interface { type TargetsTransformer struct { // List of targeted resource names specified by the user Targets []addrs.Targetable - - // 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 } func (t *TargetsTransformer) Transform(g *Graph) error { @@ -133,25 +127,27 @@ func (t *TargetsTransformer) nodeIsTarget(v dag.Vertex, targets []addrs.Targetab vertexAddr = r.ResourceInstanceAddr() case GraphNodeConfigResource: vertexAddr = r.ResourceAddr() + default: // Only resource and resource instance nodes can be targeted. return false } for _, targetAddr := range targets { - if t.IgnoreIndices { - // If we're ignoring indices then we'll convert any resource instance - // addresses into resource addresses. We don't need to convert - // vertexAddr because instance addresses are contained within - // their associated resources, and so .TargetContains will take - // care of this for us. - switch instance := targetAddr.(type) { + switch vertexAddr.(type) { + case addrs.ConfigResource: + // Before expansion happens, we only have nodes that know their + // ConfigResource address. We need to take the more specific + // target addresses and generalize them in order to compare with a + // ConfigResource. + switch target := targetAddr.(type) { case addrs.AbsResourceInstance: - targetAddr = instance.ContainingResource().Config() - case addrs.ModuleInstance: - targetAddr = instance.Module() + targetAddr = target.ContainingResource().Config() + case addrs.AbsResource: + targetAddr = target.Config() } } + if targetAddr.TargetContains(vertexAddr) { return true }