diff --git a/pkg/iac/scanners/terraform/parser/evaluator.go b/pkg/iac/scanners/terraform/parser/evaluator.go index c5ebad261236..811f618787b5 100644 --- a/pkg/iac/scanners/terraform/parser/evaluator.go +++ b/pkg/iac/scanners/terraform/parser/evaluator.go @@ -125,12 +125,13 @@ func (e *evaluator) exportOutputs() cty.Value { func (e *evaluator) EvaluateAll(ctx context.Context) (terraform.Modules, map[string]fs.FS) { - fsKey := types.CreateFSKey(e.filesystem) + e.logger.Debug("Starting module evaluation...", log.String("path", e.modulePath)) - fsMap := make(map[string]fs.FS) - fsMap[fsKey] = e.filesystem + fsKey := types.CreateFSKey(e.filesystem) + fsMap := map[string]fs.FS{ + fsKey: e.filesystem, + } - e.logger.Debug("Starting module evaluation...") e.evaluateSteps() // expand out resources and modules via count, for-each and dynamic @@ -138,9 +139,25 @@ func (e *evaluator) EvaluateAll(ctx context.Context) (terraform.Modules, map[str e.blocks = e.expandBlocks(e.blocks) e.blocks = e.expandBlocks(e.blocks) - e.logger.Debug("Starting submodule evaluation...") + submodules := e.evaluateSubmodules(ctx, fsMap) + + e.logger.Debug("Starting post-submodules evaluation...") + e.evaluateSteps() + + e.logger.Debug("Module evaluation complete.") + rootModule := terraform.NewModule(e.projectRootPath, e.modulePath, e.blocks, e.ignores) + return append(terraform.Modules{rootModule}, submodules...), fsMap +} + +func (e *evaluator) evaluateSubmodules(ctx context.Context, fsMap map[string]fs.FS) terraform.Modules { submodules := e.loadSubmodules(ctx) + if len(submodules) == 0 { + return nil + } + + e.logger.Debug("Starting submodules evaluation...") + for i := 0; i < maxContextIterations; i++ { changed := false for _, sm := range submodules { @@ -162,9 +179,7 @@ func (e *evaluator) EvaluateAll(ctx context.Context) (terraform.Modules, map[str } e.logger.Debug("Finished processing submodule(s).", log.Int("count", len(modules))) - e.logger.Debug("Module evaluation complete.") - rootModule := terraform.NewModule(e.projectRootPath, e.modulePath, e.blocks, e.ignores) - return append(terraform.Modules{rootModule}, modules...), fsMap + return modules } type submodule struct { @@ -285,6 +300,7 @@ func isBlockSupportsForEachMetaArgument(block *terraform.Block) bool { } func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool) terraform.Blocks { + var forEachFiltered terraform.Blocks for _, block := range blocks { @@ -299,6 +315,10 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool forEachVal := forEachAttr.Value() if forEachVal.IsNull() || !forEachVal.IsKnown() || !forEachAttr.IsIterable() { + e.logger.Error(`Failed to expand block. Invalid "for-each" argument. Must be known and iterable.`, + log.String("block", block.FullName()), + log.String("value", forEachVal.GoString()), + ) continue } @@ -313,7 +333,7 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool idx, err := convert.Convert(key, cty.String) if err != nil { e.logger.Error( - `Invalid "for-each" argument: map key (or set value) is not a string`, + `Failed to expand block. Invalid "for-each" argument: map key (or set value) is not a string`, log.String("block", block.FullName()), log.String("key", key.GoString()), log.String("value", val.GoString()), @@ -330,7 +350,7 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool stringVal, err := convert.Convert(val, cty.String) if err != nil { e.logger.Error( - "Failed to convert for-each arg to string", + "Failed to expand block. Invalid 'for-each' argument: value is not a string", log.String("block", block.FullName()), log.String("key", idx.AsString()), log.String("value", val.GoString()), @@ -360,7 +380,8 @@ func (e *evaluator) expandBlockForEaches(blocks terraform.Blocks, isDynamic bool ctx.Set(idx, refs[0].TypeLabel(), "key") ctx.Set(val, refs[0].TypeLabel(), "value") } else { - e.logger.Debug("Ignoring iterator attribute in dynamic block, expected one reference", log.Int("refs", len(refs))) + e.logger.Debug("Ignoring iterator attribute in dynamic block, expected one reference", + log.Int("refs", len(refs))) } } } @@ -424,7 +445,7 @@ func (e *evaluator) expandBlockCounts(blocks terraform.Blocks) terraform.Blocks e.ctx.SetByDot(cty.TupleVal(clones), metadata.Reference()) } e.logger.Debug( - "Expanded block '%s' into %d clones via 'count' attribute.", + "Expanded block into clones via 'count' attribute.", log.String("block", block.FullName()), log.Int("clones", len(clones)), ) diff --git a/pkg/iac/scanners/terraform/parser/load_module.go b/pkg/iac/scanners/terraform/parser/load_module.go index 1e96f2184b27..e23af72b6f19 100644 --- a/pkg/iac/scanners/terraform/parser/load_module.go +++ b/pkg/iac/scanners/terraform/parser/load_module.go @@ -78,7 +78,7 @@ func (e *evaluator) loadModule(ctx context.Context, b *terraform.Block) (*Module } if def, err := e.loadModuleFromTerraformCache(ctx, b, source); err == nil { - e.logger.Debug("found module in .terraform/modules", log.String("source", source)) + e.logger.Debug("Using module from Terraform cache .terraform/modules", log.String("source", source)) return def, nil } diff --git a/pkg/iac/scanners/terraform/parser/parser.go b/pkg/iac/scanners/terraform/parser/parser.go index 98efc6d43172..ea043f31e87e 100644 --- a/pkg/iac/scanners/terraform/parser/parser.go +++ b/pkg/iac/scanners/terraform/parser/parser.go @@ -161,7 +161,7 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { if p.stopOnHCLError { return err } - p.logger.Error("Error parsing", log.FilePath(path), log.Err(err)) + p.logger.Error("Error parsing file", log.FilePath(path), log.Err(err)) continue } } @@ -172,7 +172,7 @@ func (p *Parser) ParseFS(ctx context.Context, dir string) error { var ErrNoFiles = errors.New("no files found") func (p *Parser) Load(ctx context.Context) (*evaluator, error) { - p.logger.Debug("Evaluating module...") + p.logger.Debug("Loading module", log.String("module", p.moduleName)) if len(p.files) == 0 { p.logger.Info("No files found, nothing to do.") @@ -197,6 +197,17 @@ func (p *Parser) Load(ctx context.Context) (*evaluator, error) { return nil, err } p.logger.Debug("Added input variables from tfvars", log.Int("count", len(inputVars))) + + for _, varBlock := range blocks.OfType("variable") { + if varBlock.GetAttribute("default") == nil { + if _, ok := inputVars[varBlock.TypeLabel()]; !ok { + p.logger.Warn( + "Variable was not found in the environment or variable files. Evaluating may not work correctly.", + log.String("variable", varBlock.TypeLabel()), + ) + } + } + } } modulesMetadata, metadataPath, err := loadModuleMetadata(p.moduleFS, p.projectRoot) diff --git a/pkg/iac/scanners/terraform/scanner.go b/pkg/iac/scanners/terraform/scanner.go index 20c845197607..7c6a7635c5ff 100644 --- a/pkg/iac/scanners/terraform/scanner.go +++ b/pkg/iac/scanners/terraform/scanner.go @@ -152,7 +152,7 @@ func (s *Scanner) ScanFS(ctx context.Context, target fs.FS, dir string) (scan.Re sort.Strings(modulePaths) if len(modulePaths) == 0 { - s.logger.Info("No modules found, skipping scanning") + s.logger.Info("No modules found, skipping directory", log.FilePath(dir)) return nil, nil }