Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions internal/terraform/context_import_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,47 @@ func TestContextImport_33572(t *testing.T) {
}
}

// Missing import target should produce an error
func TestContextImport_missingModuleImport(t *testing.T) {
p := testProvider("test")
m := testModuleInline(t, map[string]string{
"main.tf": `
locals {
xs = toset(["foo"])
}

module "a" {
for_each = local.xs
source = "./a"
}

import {
to = module.WRONG.test_resource.x
id = "id"
}
`,
"a/main.tf": `
resource "test_resource" "x" {
value = "z"
}
`,
})

ctx := testContext2(t, &ContextOpts{
Providers: map[addrs.Provider]providers.Factory{
addrs.NewDefaultProvider("test"): testProviderFuncFixed(p),
},
})

_, diags := ctx.Plan(m, states.NewState(), DefaultPlanOpts)
if !diags.HasErrors() {
t.Fatal("expected missing import target error")
}
if !strings.Contains(diags.Err().Error(), "Configuration for import target does not exist") {
t.Fatalf("incorrect error for missing import target: %s\n", diags.Err())
}
}

const testImportStr = `
aws_instance.foo:
ID = foo
Expand Down
54 changes: 46 additions & 8 deletions internal/terraform/transform_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ func (t *ConfigTransformer) Transform(g *Graph) error {
return nil
}

if err := t.validateImportTargets(); err != nil {
return err
}

// Start the transformation process
return t.transform(g, t.Config)
}
Expand Down Expand Up @@ -173,13 +177,6 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er

// If any import targets were not claimed by resources and we are
// generating configuration, then let's add them into the graph now.

// TODO: use diagnostics to collect detailed errors for now, even though we
// can only return an error from here. This gives the user more immediate
// feedback, rather than waiting an unknown amount of time for the plan to
// fail.
var diags tfdiags.Diagnostics

for _, i := range importTargets {
if path.IsRoot() {
// If we have a single instance import target in the root module, we
Expand Down Expand Up @@ -212,8 +209,49 @@ func (t *ConfigTransformer) transformSingle(g *Graph, config *configs.Config) er
g.Add(node)
continue
}
}
}
return nil
}

// validateImportTargets ensures that the import target resources exist in the
// configuration. We do this here rather than statically during config loading
// to have any CLI imports included, and to provide feedback about possible
// config generation.
func (t *ConfigTransformer) validateImportTargets() error {
var diags tfdiags.Diagnostics

for _, i := range t.importTargets {
var toResource addrs.ConfigResource
switch {
case i.Config != nil:
toResource = i.Config.ToResource
default:
toResource = i.LegacyAddr.ConfigResource()
}

moduleCfg := t.Config.Root.Descendent(toResource.Module)
if moduleCfg != nil {
res := moduleCfg.Module.ResourceByAddr(toResource.Resource)
if res != nil {
// the target config exists, which is all we're looking for at this point.
continue
}
}

if toResource.Module.IsRoot() {
var toDiags tfdiags.Diagnostics
traversal, hd := hcl.AbsTraversalForExpr(i.Config.To)
toDiags = toDiags.Append(hd)
to, td := addrs.ParseAbsResourceInstance(traversal)
toDiags = toDiags.Append(td)
canGenerate := !toDiags.HasErrors() && to.Resource.Key == addrs.NoKey

if t.generateConfigPathForImportTargets != "" {
if canGenerate {
continue
}

if t.generateConfigPathForImportTargets != "" && !canGenerate {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot generate configuration",
Expand Down