From a4c8762d74db40afcc205fcb1d1ba225acfd4a6a Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Fri, 7 Aug 2026 10:58:51 -0400 Subject: [PATCH] import: Fix expansion panic with matching child module + root module import targets --- .../terraform/context_plan_import_test.go | 46 +++++++++++++++++++ internal/terraform/transform_config.go | 4 +- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/internal/terraform/context_plan_import_test.go b/internal/terraform/context_plan_import_test.go index 40662f016665..cce43fa2b520 100644 --- a/internal/terraform/context_plan_import_test.go +++ b/internal/terraform/context_plan_import_test.go @@ -2513,3 +2513,49 @@ resource "test_object" "a" {} t.Fatal("Wrong, but impressive - how did you even defer the wrong resource?") } } + +// This is a regression test for an expansion panic during plan where the +// child import block registers an expansion in the root module, which would +// then cause an "expansion already registered for " panic. +func TestContextPlan_import_in_module_matches_root_to_addr(t *testing.T) { + m := testModuleInline(t, map[string]string{ + "main.tf": ` +module "child" { + source = "./child" +} +import { + to = test_object.samename + id = "test-a" +} + `, + "child/main.tf": ` +import { + to = test_object.samename + id = "test-b" +} + `, + }) + + ctx := testContext2(t, &ContextOpts{ + Providers: map[addrs.Provider]providers.Factory{ + addrs.NewDefaultProvider("test"): testProviderFuncFixed(simpleMockProvider()), + }, + }) + + validateDiags := ctx.Validate(m, nil) + + wantErr := "module.child.test_object.samename not found. Only resources within the root module are eligible for config generation." + if !validateDiags.HasErrors() { + t.Errorf("unexpected success from validate\nwant: message containing %q", wantErr) + } else if got, want := validateDiags.Err().Error(), wantErr; !strings.Contains(got, want) { + t.Errorf("wrong error from validate:\ngot: %s\nwant: message containing %q", got, want) + } + + // The expected error is only raised from validate, but the panic this regression test + // covers happens during expansion which is occurs in the plan. + // + // Since this isn't valid configuration we only care that plan doesn't panic. + ctx.Plan(m, states.NewState(), &PlanOpts{ + Mode: plans.NormalMode, + }) +} diff --git a/internal/terraform/transform_config.go b/internal/terraform/transform_config.go index d5e3e62d5ef9..c32fafe3bd9e 100644 --- a/internal/terraform/transform_config.go +++ b/internal/terraform/transform_config.go @@ -241,13 +241,13 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er // generating configuration. Add them to the graph for validation. for _, i := range importTargets { - log.Printf("[DEBUG] ConfigTransformer: adding config generation node for %s", i.Config.ToResource) + log.Printf("[DEBUG] ConfigTransformer: adding config generation node for %s", i.AbsToConfigResource) // TODO: if config generation is ever supported for for_each // resources, this will add multiple nodes for the same // resource abstract := &NodeAbstractResource{ - Addr: i.Config.ToResource, + Addr: i.AbsToConfigResource, importTargets: []*ImportTarget{i}, generateConfigPath: t.generateConfigPathForImportTargets, }