diff --git a/cli/args.go b/cli/args.go index 026bf5b45..0a9748140 100644 --- a/cli/args.go +++ b/cli/args.go @@ -149,6 +149,8 @@ func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, wri return nil, err } + validateStrictMode := parseBooleanArg(args, OPT_TERRAGRUNT_STRICT_VALIDATE, false) + opts, err := options.NewTerragruntOptions(filepath.ToSlash(terragruntConfigPath)) if err != nil { return nil, err @@ -177,6 +179,7 @@ func parseTerragruntOptionsFromArgs(terragruntVersion string, args []string, wri opts.WorkingDir = filepath.ToSlash(workingDir) opts.DownloadDir = filepath.ToSlash(downloadDir) opts.LogLevel = loggingLevel + opts.ValidateStrict = validateStrictMode opts.Logger = util.CreateLogEntry("", loggingLevel) opts.Logger.Logger.SetOutput(errWriter) opts.RunTerragrunt = RunTerragrunt diff --git a/cli/cli_app.go b/cli/cli_app.go index c8a2d1dc0..8bf2bd3b6 100644 --- a/cli/cli_app.go +++ b/cli/cli_app.go @@ -50,6 +50,7 @@ const OPT_TERRAGRUNT_HCLFMT_FILE = "terragrunt-hclfmt-file" const OPT_TERRAGRUNT_DEBUG = "terragrunt-debug" const OPT_TERRAGRUNT_OVERRIDE_ATTR = "terragrunt-override-attr" const OPT_TERRAGRUNT_LOGLEVEL = "terragrunt-log-level" +const OPT_TERRAGRUNT_STRICT_VALIDATE = "terragrunt-strict-validate" var ALL_TERRAGRUNT_BOOLEAN_OPTS = []string{ OPT_NON_INTERACTIVE, @@ -79,6 +80,7 @@ var ALL_TERRAGRUNT_STRING_OPTS = []string{ OPT_TERRAGRUNT_HCLFMT_FILE, OPT_TERRAGRUNT_OVERRIDE_ATTR, OPT_TERRAGRUNT_LOGLEVEL, + OPT_TERRAGRUNT_STRICT_VALIDATE, } const CMD_INIT = "init" @@ -235,6 +237,7 @@ GLOBAL OPTIONS: terragrunt-override-attr A key=value attribute to override in a provider block as part of the aws-provider-patch command. May be specified multiple times. terragrunt-debug Write terragrunt-debug.tfvars to working folder to help root-cause issues. terragrunt-log-level Sets the logging level for Terragrunt. Supported levels: panic, fatal, error, warn (default), info, debug, trace. + terragrunt-strict-validate Sets strict mode for the validate-inputs command. By default, strict mode is off. When this flag is passed, strict mode is turned on. When strict mode is turned off, the validate-inputs command will only return an error if required inputs are missing from all input sources (env vars, var files, etc). When strict mode is turned on, an error will be returned if required inputs are missing OR if unused variables are passed to Terragrunt. VERSION: {{.Version}}{{if len .Authors}} diff --git a/cli/validate_inputs.go b/cli/validate_inputs.go index e73d335e6..5f078a98b 100644 --- a/cli/validate_inputs.go +++ b/cli/validate_inputs.go @@ -54,6 +54,7 @@ func validateTerragruntInputs(terragruntOptions *options.TerragruntOptions, work terragruntOptions.Logger.Warn("") } else { terragruntOptions.Logger.Info("All variables passed in by terragrunt are in use.") + terragruntOptions.Logger.Debug(fmt.Sprintf("Strict mode enabled: %t", terragruntOptions.ValidateStrict)) } if len(missingVars) > 0 { @@ -63,13 +64,19 @@ func validateTerragruntInputs(terragruntOptions *options.TerragruntOptions, work } terragruntOptions.Logger.Error("") } else { - terragruntOptions.Logger.Info("All required inputs are passed in by terragrunt.") + terragruntOptions.Logger.Info("All required inputs are passed in by terragrunt") + terragruntOptions.Logger.Debug(fmt.Sprintf("Strict mode enabled: %t", terragruntOptions.ValidateStrict)) } - // Return an error when there are misaligned inputs. - if len(unusedVars) > 0 || len(missingVars) > 0 { - return fmt.Errorf("Terragrunt configuration has misaligned inputs") + // Return an error when there are misaligned inputs. Terragrunt strict mode defaults to false. When it is false, + // an error will only be returned if required inputs are missing. When strict mode is true, an error will be + // returned if required inputs are missing OR if any unused variables are passed + if len(missingVars) > 0 || len(unusedVars) > 0 && terragruntOptions.ValidateStrict { + return fmt.Errorf(fmt.Sprintf("Terragrunt configuration has misaligned inputs. Strict mode enabled: %t.", terragruntOptions.ValidateStrict)) + } else if len(unusedVars) > 0 { + terragruntOptions.Logger.Warn("Terragrunt configuration has misaligned inputs, but running in relaxed mode so ignoring.") } + return nil } diff --git a/docs/_docs/04_reference/cli-options.md b/docs/_docs/04_reference/cli-options.md index a0ca48922..9a5ca189c 100644 --- a/docs/_docs/04_reference/cli-options.md +++ b/docs/_docs/04_reference/cli-options.md @@ -406,6 +406,7 @@ prefix `--terragrunt-` (e.g., `--terragrunt-config`). The currently available op - [terragrunt-exclude-dir](#terragrunt-exclude-dir) - [terragrunt-include-dir](#terragrunt-include-dir) - [terragrunt-strict-include](#terragrunt-strict-include) +- [terragrunt-strict-validate](#terragrunt-strict-validate) - [terragrunt-ignore-dependency-order](#terragrunt-ignore-dependency-order) - [terragrunt-ignore-external-dependencies](#terragrunt-ignore-external-dependencies) - [terragrunt-include-external-dependencies](#terragrunt-include-external-dependencies) @@ -610,6 +611,11 @@ will be included. All dependencies of the included directories will be excluded directories. If no [--terragrunt-include-dir](#terragrunt-include-dir) flags are included, terragrunt will not include any modules during the execution of the commands. +### terragrunt-strict-validate + +**CLI Arg**: `--terragrunt-strict-validate` + +When passed in, and running `terragrunt validate-inputs`, enables strict mode for the `validate-inputs` command. When strict mode is enabled, an error will be returned if any variables required by the underlying Terraform configuration are not passed in, OR if any unused variables are passed in. By default, `terragrunt validate-inputs` runs in relaxed mode. In relaxed mode, an error is only returned when a variable required by the underlying Terraform configuration is not passed in. ### terragrunt-ignore-dependency-order diff --git a/options/options.go b/options/options.go index 1a99cce45..390afba58 100644 --- a/options/options.go +++ b/options/options.go @@ -86,6 +86,9 @@ type TerragruntOptions struct { // Log level LogLevel logrus.Level + // ValidateStrict mode for the validate-inputs command + ValidateStrict bool + // Environment variables at runtime Env map[string]string @@ -197,6 +200,7 @@ func NewTerragruntOptions(terragruntConfigPath string) (*TerragruntOptions, erro WorkingDir: workingDir, Logger: logger, LogLevel: DEFAULT_LOG_LEVEL, + ValidateStrict: false, Env: map[string]string{}, Source: "", SourceMap: map[string]string{}, diff --git a/test/integration_debug_test.go b/test/integration_debug_test.go index 331af8fbf..6d6fbe538 100644 --- a/test/integration_debug_test.go +++ b/test/integration_debug_test.go @@ -79,7 +79,7 @@ func TestTerragruntValidateInputs(t *testing.T) { t.Parallel() nameDashSplit := strings.Split(name, "-") - runTerragruntValidateInputs(t, module, nil, nameDashSplit[0] == "success") + runTerragruntValidateInputs(t, module, []string{"--terragrunt-strict-validate"}, nameDashSplit[0] == "success") }) } } @@ -103,6 +103,46 @@ func TestTerragruntValidateInputsWithCLIVarFile(t *testing.T) { runTerragruntValidateInputs(t, moduleDir, args, true) } +func TestTerragruntValidateInputsWithStrictMode(t *testing.T) { + t.Parallel() + + moduleDir := filepath.Join("fixture-validate-inputs", "success-inputs-only") + args := []string{"--terragrunt-strict-validate"} + runTerragruntValidateInputs(t, moduleDir, args, true) +} + +func TestTerragruntValidateInputsWithStrictModeDisabledAndUnusedVar(t *testing.T) { + t.Parallel() + + moduleDir := filepath.Join("fixture-validate-inputs", "success-inputs-only") + args := []string{"-var=testvariable=testvalue"} + runTerragruntValidateInputs(t, moduleDir, args, true) +} + +func TestTerragruntValidateInputsWithStrictModeEnabledAndUnusedVar(t *testing.T) { + t.Parallel() + + moduleDir := filepath.Join("fixture-validate-inputs", "success-inputs-only") + args := []string{"-var=testvariable=testvalue", "--terragrunt-strict-validate"} + runTerragruntValidateInputs(t, moduleDir, args, false) +} + +func TestTerragruntValidateInputsWithStrictModeEnabledAndUnusedInputs(t *testing.T) { + t.Parallel() + + moduleDir := filepath.Join("fixture-validate-inputs", "fail-unused-inputs") + args := []string{"--terragrunt-strict-validate"} + runTerragruntValidateInputs(t, moduleDir, args, false) +} + +func TestTerragruntValidateInputsWithStrictModeDisabledAndUnusedInputs(t *testing.T) { + t.Parallel() + + moduleDir := filepath.Join("fixture-validate-inputs", "fail-unused-inputs") + args := []string{} + runTerragruntValidateInputs(t, moduleDir, args, true) +} + func runTerragruntValidateInputs(t *testing.T, moduleDir string, extraArgs []string, isSuccessTest bool) { maybeNested := filepath.Join(moduleDir, "module") if util.FileExists(maybeNested) { diff --git a/test/integration_serial_test.go b/test/integration_serial_test.go index 50e90a607..3e573b2c1 100644 --- a/test/integration_serial_test.go +++ b/test/integration_serial_test.go @@ -205,7 +205,8 @@ func TestTerragruntValidateInputsWithUnusedEnvVar(t *testing.T) { defer os.Unsetenv("TF_VAR_unused") moduleDir := filepath.Join("fixture-validate-inputs", "success-inputs-only") - runTerragruntValidateInputs(t, moduleDir, nil, false) + args := []string{"--terragrunt-strict-validate"} + runTerragruntValidateInputs(t, moduleDir, args, false) } func TestTerragruntSourceMapEnvArg(t *testing.T) {