diff --git a/pkg/iac/scanners/terraform/parser/load_module.go b/pkg/iac/scanners/terraform/parser/load_module.go index 78ebe3430b4e..968f73df0673 100644 --- a/pkg/iac/scanners/terraform/parser/load_module.go +++ b/pkg/iac/scanners/terraform/parser/load_module.go @@ -90,9 +90,9 @@ func (e *evaluator) loadModuleFromTerraformCache(ctx context.Context, b *terrafo var modulePath string if e.moduleMetadata != nil { // if we have module metadata we can parse all the modules as they'll be cached locally! - name := b.ModuleName() + moduleKey := b.ModuleKey() for _, module := range e.moduleMetadata.Modules { - if module.Key == name { + if module.Key == moduleKey { modulePath = path.Clean(path.Join(e.projectRootPath, module.Dir)) break } diff --git a/pkg/iac/scanners/terraform/parser/parser_test.go b/pkg/iac/scanners/terraform/parser/parser_test.go index 49976bd8bb55..e88dd017d2fa 100644 --- a/pkg/iac/scanners/terraform/parser/parser_test.go +++ b/pkg/iac/scanners/terraform/parser/parser_test.go @@ -2010,6 +2010,61 @@ variable "baz" {} assert.Contains(t, buf.String(), "variables=\"foo\"") } +func TestLoadChildModulesFromLocalCache(t *testing.T) { + var buf bytes.Buffer + slog.SetDefault(slog.New(log.NewHandler(&buf, &log.Options{Level: log.LevelDebug}))) + + fsys := fstest.MapFS{ + "main.tf": &fstest.MapFile{Data: []byte(`module "level_1" { + source = "./modules/level_1" +}`)}, + "modules/level_1/main.tf": &fstest.MapFile{Data: []byte(`module "level_2" { + source = "../level_2" +}`)}, + "modules/level_2/main.tf": &fstest.MapFile{Data: []byte(`module "level_3" { + count = 2 + source = "../level_3" +}`)}, + "modules/level_3/main.tf": &fstest.MapFile{Data: []byte(`resource "foo" "bar" {}`)}, + ".terraform/modules/modules.json": &fstest.MapFile{Data: []byte(`{ + "Modules": [ + { "Key": "", "Source": "", "Dir": "." }, + { + "Key": "level_1", + "Source": "./modules/level_1", + "Dir": "modules/level_1" + }, + { + "Key": "level_1.level_2", + "Source": "../level_2", + "Dir": "modules/level_2" + }, + { + "Key": "level_1.level_2.level_3", + "Source": "../level_3", + "Dir": "modules/level_3" + } + ] +}`)}, + } + + parser := New( + fsys, "", + OptionStopOnHCLError(true), + ) + require.NoError(t, parser.ParseFS(context.TODO(), ".")) + + modules, _, err := parser.EvaluateAll(context.TODO()) + require.NoError(t, err) + + assert.Len(t, modules, 5) + + assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"./modules/level_1\"") + assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"../level_2\"") + assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"../level_3\"") + assert.Contains(t, buf.String(), "Using module from Terraform cache .terraform/modules\tsource=\"../level_3\"") +} + func TestLogParseErrors(t *testing.T) { var buf bytes.Buffer slog.SetDefault(slog.New(log.NewHandler(&buf, nil))) diff --git a/pkg/iac/terraform/block.go b/pkg/iac/terraform/block.go index 13175bb9a072..a6cd6ab1bbc4 100644 --- a/pkg/iac/terraform/block.go +++ b/pkg/iac/terraform/block.go @@ -480,22 +480,12 @@ func (b *Block) FullName() string { return b.LocalName() } -func (b *Block) ModuleName() string { - name := strings.TrimPrefix(b.LocalName(), "module.") - if b.moduleBlock != nil { - module := strings.TrimPrefix(b.moduleBlock.FullName(), "module.") - name = fmt.Sprintf( - "%s.%s", - module, - name, - ) - } - var parts []string - for _, part := range strings.Split(name, ".") { - part = strings.Split(part, "[")[0] - parts = append(parts, part) +func (b *Block) ModuleKey() string { + name := b.Reference().NameLabel() + if b.moduleBlock == nil { + return name } - return strings.Join(parts, ".") + return fmt.Sprintf("%s.%s", b.moduleBlock.ModuleKey(), name) } func (b *Block) UniqueName() string {