From e085a3c180b36dcc43a6bcbd7b3ab262b679a25b Mon Sep 17 00:00:00 2001 From: Kristin Laemmert Date: Tue, 14 Apr 2026 08:45:33 -0400 Subject: [PATCH] use the correct context when evaluating imports --- internal/refactoring/import_statement.go | 2 +- .../terraform/context_apply_import_test.go | 44 ++++++++++++++++--- internal/terraform/node_resource_plan.go | 3 ++ .../import-block-in-module/child/main.tf | 17 +++++-- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/internal/refactoring/import_statement.go b/internal/refactoring/import_statement.go index 277c171710..ac2cafd316 100644 --- a/internal/refactoring/import_statement.go +++ b/internal/refactoring/import_statement.go @@ -17,7 +17,7 @@ type ImportStatement struct { // FindImportStatements recurses through the modules of the given configuration // and returns a set of all "import" blocks defined within after deduplication -// on the From address. +// on the To address. // // An "import" block in a parent module overrides an import block in a child // module when both target the same configuration object. diff --git a/internal/terraform/context_apply_import_test.go b/internal/terraform/context_apply_import_test.go index bed35edec9..df573c1201 100644 --- a/internal/terraform/context_apply_import_test.go +++ b/internal/terraform/context_apply_import_test.go @@ -4,11 +4,13 @@ package terraform import ( + "encoding/json" "testing" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/addrs" + "github.com/hashicorp/terraform/internal/configs/configschema" "github.com/hashicorp/terraform/internal/plans" "github.com/hashicorp/terraform/internal/providers" "github.com/hashicorp/terraform/internal/states" @@ -19,7 +21,12 @@ import ( func TestContextApply_import_in_module(t *testing.T) { m := testModule(t, "import-block-in-module") - p := simpleMockProvider() + p := mockProviderWithResourceTypeSchema("test_object", &configschema.Block{ + Attributes: map[string]*configschema.Attribute{ + "id": {Type: cty.String, Computed: true}, + "test_string": {Type: cty.String, Optional: true}, + }, + }) p.ImportResourceStateFn = func(req providers.ImportResourceStateRequest) providers.ImportResourceStateResponse { return providers.ImportResourceStateResponse{ ImportedResources: []providers.ImportedResource{ @@ -27,15 +34,20 @@ func TestContextApply_import_in_module(t *testing.T) { TypeName: "test_object", State: cty.ObjectVal(map[string]cty.Value{ "test_string": cty.StringVal("importable"), + "id": cty.StringVal(req.ID), }), }, }, } } - p.ReadResourceResponse = &providers.ReadResourceResponse{ - NewState: cty.ObjectVal(map[string]cty.Value{ - "test_string": cty.StringVal("importable"), - }), + p.ReadResourceFn = func(r providers.ReadResourceRequest) providers.ReadResourceResponse { + id := r.PriorState.GetAttr("id") + return providers.ReadResourceResponse{ + NewState: cty.ObjectVal(map[string]cty.Value{ + "test_string": cty.StringVal("importable"), + "id": id, + }), + } } ctx := testContext2(t, &ContextOpts{ @@ -56,10 +68,30 @@ func TestContextApply_import_in_module(t *testing.T) { t.Fatal("resource not imported") } - rs := state.ResourceInstance(mustResourceInstanceAddr("module.child.test_object.bar")) + rs := state.ResourceInstance(mustResourceInstanceAddr("module.child.test_object.bar[\"first\"]")) if rs == nil { t.Fatal("imported resource not found in module") } + var attrs map[string]interface{} + err := json.Unmarshal(rs.Current.AttrsJSON, &attrs) + if err != nil { + t.Fatal(err) + } + if got, want := attrs["id"], "testa"; got != want { + t.Fatalf("wrong id for \"first\" got: %#v\nwant: %#v", got, want) + } + + rs = state.ResourceInstance(mustResourceInstanceAddr("module.child.test_object.bar[\"second\"]")) + if rs == nil { + t.Fatal("imported resource not found in module") + } + err = json.Unmarshal(rs.Current.AttrsJSON, &attrs) + if err != nil { + t.Fatal(err) + } + if got, want := attrs["id"], "testb"; got != want { + t.Fatalf("wrong id for \"second\" got: %#v\nwant: %#v", got, want) + } } func TestContextApply_import_in_nested_module(t *testing.T) { // more nested than the test above. nesteder. diff --git a/internal/terraform/node_resource_plan.go b/internal/terraform/node_resource_plan.go index f72f07900c..609ddb91c1 100644 --- a/internal/terraform/node_resource_plan.go +++ b/internal/terraform/node_resource_plan.go @@ -178,6 +178,9 @@ func (n *nodeExpandPlannableResource) expandResourceImports(ctx EvalContext, all // do I need to get all possible expansions for the imp.RelModule and then only use the one for this actual node? allMods := ctx.InstanceExpander().AllInstances().InstancesForModule(imp.RelModule, false) for _, mod := range allMods { + // get the context from the module the import was defined in + ctx = evalContextForModuleInstance(ctx, mod) + state = ctx.State() if imp.Config.ForEach == nil { traversal, hds := hcl.AbsTraversalForExpr(imp.Config.To) diags = diags.Append(hds) diff --git a/internal/terraform/testdata/import-block-in-module/child/main.tf b/internal/terraform/testdata/import-block-in-module/child/main.tf index 8712143314..0d0d78e7ae 100644 --- a/internal/terraform/testdata/import-block-in-module/child/main.tf +++ b/internal/terraform/testdata/import-block-in-module/child/main.tf @@ -1,7 +1,16 @@ -import { - to = test_object.bar - id = "importable" +locals { + ids = { + first = "testa" + second = "testb" + } +} + +resource test_object bar { + for_each = local.ids } -resource "test_object" "bar" { +import { + for_each = local.ids + to = test_object.bar[each.key] + id = each.value } \ No newline at end of file