diff --git a/internal/configs/configload/loader_load_test.go b/internal/configs/configload/loader_load_test.go index ee2488c792ba..d285cc9f1656 100644 --- a/internal/configs/configload/loader_load_test.go +++ b/internal/configs/configload/loader_load_test.go @@ -105,6 +105,33 @@ func TestLoaderLoadConfig_loadDiags(t *testing.T) { } } +func TestLoaderLoadConfig_loadDiagsFromSubmodules(t *testing.T) { + // building a config which didn't load correctly may cause configs to panic + fixtureDir := filepath.Clean("testdata/invalid-names-in-submodules") + loader, err := NewLoader(&Config{ + ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), + }) + if err != nil { + t.Fatalf("unexpected error from NewLoader: %s", err) + } + + cfg, diags := loader.LoadConfig(fixtureDir) + if !diags.HasErrors() { + t.Fatalf("loading succeeded; want an error") + } + if got, want := diags.Error(), " Invalid provider local name"; !strings.Contains(got, want) { + t.Errorf("missing expected error\nwant substring: %s\ngot: %s", want, got) + } + + if cfg == nil { + t.Fatal("partial config not returned with diagnostics") + } + + if cfg.Module == nil { + t.Fatal("expected config module") + } +} + func TestLoaderLoadConfig_childProviderGrandchildCount(t *testing.T) { // This test is focused on the specific situation where: // - A child module contains a nested provider block, which is no longer diff --git a/internal/configs/configload/testdata/invalid-names-in-submodules/.terraform/modules/modules.json b/internal/configs/configload/testdata/invalid-names-in-submodules/.terraform/modules/modules.json new file mode 100644 index 000000000000..c55c1cf54ff0 --- /dev/null +++ b/internal/configs/configload/testdata/invalid-names-in-submodules/.terraform/modules/modules.json @@ -0,0 +1,14 @@ +{ + "Modules": [ + { + "Key": "test", + "Source": "./sub", + "Dir": "testdata/invalid-names-in-submodules/sub" + }, + { + "Key": "", + "Source": "", + "Dir": "." + } + ] +} \ No newline at end of file diff --git a/internal/configs/configload/testdata/invalid-names-in-submodules/main.tf b/internal/configs/configload/testdata/invalid-names-in-submodules/main.tf new file mode 100644 index 000000000000..3fbc8c68cf0c --- /dev/null +++ b/internal/configs/configload/testdata/invalid-names-in-submodules/main.tf @@ -0,0 +1,3 @@ +module "test" { + source = "./sub" +} diff --git a/internal/configs/configload/testdata/invalid-names-in-submodules/sub/main.tf b/internal/configs/configload/testdata/invalid-names-in-submodules/sub/main.tf new file mode 100644 index 000000000000..aacab2c441dd --- /dev/null +++ b/internal/configs/configload/testdata/invalid-names-in-submodules/sub/main.tf @@ -0,0 +1,7 @@ +resource "aws-_foo" "test" { + +} + +data "aws-_bar" "test" { + +} diff --git a/internal/configs/provider_validation.go b/internal/configs/provider_validation.go index facd92520ce2..0cf7378d3639 100644 --- a/internal/configs/provider_validation.go +++ b/internal/configs/provider_validation.go @@ -153,6 +153,18 @@ func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConf } localName := r.Addr().ImpliedProvider() + + _, err := addrs.ParseProviderPart(localName) + if err != nil { + diags = append(diags, &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Invalid provider local name", + Detail: fmt.Sprintf("%q is an invalid implied provider local name: %s", localName, err), + Subject: r.DeclRange.Ptr(), + }) + continue + } + if _, ok := localNames[localName]; ok { // OK, this was listed directly in the required_providers continue