diff --git a/internal/terraform/context_plan2_test.go b/internal/terraform/context_plan2_test.go index 787e8d34cd..2979991e71 100644 --- a/internal/terraform/context_plan2_test.go +++ b/internal/terraform/context_plan2_test.go @@ -4709,6 +4709,97 @@ removed { } } +func TestContext2Plan_removedModuleRemovesDeeplyNestedResources(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +removed { + from = module.child + lifecycle { + destroy = false + } +} +`, + }) + + state := states.BuildState(func(s *states.SyncState) { + s.SetResourceInstanceCurrent(mustResourceInstanceAddr("module.child.test_object.a"), &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"foo":"bar"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + s.SetResourceInstanceCurrent(mustResourceInstanceAddr("module.child.module.grandchild.test_object.a"), &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"foo":"bar"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + + // We'll also check it works for deeply nested deposed objects too. + s.SetResourceInstanceDeposed(mustResourceInstanceAddr("module.child.module.grandchild.test_object.a"), "gone", &states.ResourceInstanceObjectSrc{ + AttrsJSON: []byte(`{"foo":"bar"}`), + Status: states.ObjectReady, + }, mustProviderConfig(`provider["registry.terraform.io/hashicorp/test"]`)) + }) + + p := simpleMockProvider() + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(p), + }, + }) + + plan, diags := ctx.Plan(m, state, &PlanOpts{ + Mode: plans.NormalMode, + }) + if diags.HasErrors() { + t.Errorf("expected no errors but got %s", diags.Err()) + } + + resources := []string{"module.child.test_object.a", "module.child.module.grandchild.test_object.a"} + for _, resource := range resources { + t.Run(resource, func(t *testing.T) { + addr := mustResourceInstanceAddr(resource) + + instPlan := plan.Changes.ResourceInstance(addr) + if instPlan == nil { + t.Fatalf("no plan for %s at all", addr) + } + + if got, want := instPlan.Addr, addr; !got.Equal(want) { + t.Errorf("wrong current address\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.PrevRunAddr, addr; !got.Equal(want) { + t.Errorf("wrong previous run address\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.Action, plans.Forget; got != want { + t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.ActionReason, plans.ResourceInstanceDeleteBecauseNoResourceConfig; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + }) + } + + t.Run("module.child.module.grandchild.test_object.a (deposed)", func(t *testing.T) { + addr := mustResourceInstanceAddr("module.child.module.grandchild.test_object.a") + + instPlan := plan.Changes.ResourceInstanceDeposed(addr, "gone") + if instPlan == nil { + t.Fatalf("no plan for %s at all", addr) + } + + if got, want := instPlan.Addr, addr; !got.Equal(want) { + t.Errorf("wrong current address\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.PrevRunAddr, addr; !got.Equal(want) { + t.Errorf("wrong previous run address\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.Action, plans.Forget; got != want { + t.Errorf("wrong planned action\ngot: %s\nwant: %s", got, want) + } + if got, want := instPlan.ActionReason, plans.ResourceInstanceChangeNoReason; got != want { + t.Errorf("wrong action reason\ngot: %s\nwant: %s", got, want) + } + }) +} + func TestContext2Plan_removedModuleErrorStillInConfigNested(t *testing.T) { addrA := mustResourceInstanceAddr("test_object.a") m := testModuleInline(t, map[string]string{ diff --git a/internal/terraform/node_resource_destroy_deposed.go b/internal/terraform/node_resource_destroy_deposed.go index 600b1e5908..8010dd84ef 100644 --- a/internal/terraform/node_resource_destroy_deposed.go +++ b/internal/terraform/node_resource_destroy_deposed.go @@ -144,7 +144,7 @@ func (n *NodePlanDeposedResourceInstanceObject) Execute(ctx EvalContext, op walk } } for _, fm := range n.forgetModules { - if fm.Equal(n.Addr.Module.Module()) { + if fm.TargetContains(n.Addr) { forget = true } } diff --git a/internal/terraform/node_resource_plan_orphan.go b/internal/terraform/node_resource_plan_orphan.go index 6a6b5535b9..31f8613d09 100644 --- a/internal/terraform/node_resource_plan_orphan.go +++ b/internal/terraform/node_resource_plan_orphan.go @@ -147,7 +147,7 @@ func (n *NodePlannableResourceInstanceOrphan) managedResourceExecute(ctx EvalCon } } for _, fm := range n.forgetModules { - if fm.Equal(n.Addr.Module.Module()) { + if fm.TargetContains(n.Addr) { forget = true } }