Skip to content

Commit ab330c6

Browse files
authored
fix: Fixing null input passing (#5458)
1 parent 93b0edf commit ab330c6

2 files changed

Lines changed: 77 additions & 0 deletions

File tree

internal/runner/run/run.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package run
55

66
import (
77
"context"
8+
"encoding/json"
89
"fmt"
910
"io"
1011
"maps"
@@ -35,6 +36,7 @@ import (
3536

3637
const (
3738
CommandNameTerragruntReadConfig = "terragrunt-read-config"
39+
NullTFVarsFile = ".terragrunt-null-vars.auto.tfvars.json"
3840
)
3941

4042
var TerraformCommandsThatUseState = []string{
@@ -256,6 +258,20 @@ func runTerragruntWithConfig(
256258
}
257259
}
258260

261+
// Write null-valued inputs to a tfvars.json file that OpenTofu/Terraform will auto-load.
262+
nullVarsFile, err := setTerragruntNullValuesRunCfg(opts, cfg)
263+
if err != nil {
264+
return err
265+
}
266+
267+
defer func() {
268+
if nullVarsFile != "" {
269+
if err := os.Remove(nullVarsFile); err != nil && !errors.Is(err, os.ErrNotExist) {
270+
l.Debugf("Failed to remove null values file %s: %v", nullVarsFile, err)
271+
}
272+
}
273+
}()
274+
259275
// Now that we've run 'init' and have all the source code locally, we can finally run the patch command
260276
if err := checkProtectedModuleRunCfg(opts, cfg); err != nil {
261277
return err
@@ -715,3 +731,36 @@ func checkProtectedModuleRunCfg(opts *options.TerragruntOptions, cfg *runcfg.Run
715731

716732
return nil
717733
}
734+
735+
// setTerragruntNullValuesRunCfg writes null-valued inputs to a tfvars.json file
736+
// that OpenTofu/Terraform will auto-load. This is necessary because OpenTofu/Terraform
737+
// cannot accept null values via environment variables (TF_VAR_*), but it can read them
738+
// from .auto.tfvars.json files.
739+
func setTerragruntNullValuesRunCfg(opts *options.TerragruntOptions, cfg *runcfg.RunConfig) (string, error) {
740+
jsonEmptyVars := make(map[string]any)
741+
742+
for varName, varValue := range cfg.Inputs {
743+
if varValue == nil {
744+
jsonEmptyVars[varName] = nil
745+
}
746+
}
747+
748+
if len(jsonEmptyVars) == 0 {
749+
return "", nil
750+
}
751+
752+
jsonContents, err := json.MarshalIndent(jsonEmptyVars, "", " ")
753+
if err != nil {
754+
return "", errors.New(err)
755+
}
756+
757+
varFile := filepath.Join(opts.WorkingDir, NullTFVarsFile)
758+
759+
const ownerReadWritePermissions = 0600
760+
761+
if err := os.WriteFile(varFile, jsonContents, os.FileMode(ownerReadWritePermissions)); err != nil {
762+
return "", errors.New(err)
763+
}
764+
765+
return varFile, nil
766+
}

test/integration_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4673,3 +4673,31 @@ func TestNoColorDependency(t *testing.T) {
46734673
assert.NotContains(t, stderr, "\x1b")
46744674
assert.NotContains(t, stdout, "\x1b")
46754675
}
4676+
4677+
// TestTerragruntPassNullValues verifies that terragrunt can pass null values to
4678+
// Terraform variables. This is a regression test for:
4679+
// https://github.com/gruntwork-io/terragrunt/issues/5452
4680+
func TestTerragruntPassNullValues(t *testing.T) {
4681+
t.Parallel()
4682+
4683+
tmpEnvPath := helpers.CopyEnvironment(t, testFixtureNullValue)
4684+
testPath := filepath.Join(tmpEnvPath, testFixtureNullValue)
4685+
4686+
_, _, err := helpers.RunTerragruntCommandWithOutput(
4687+
t,
4688+
"terragrunt apply -auto-approve --non-interactive --working-dir "+testPath,
4689+
)
4690+
require.NoError(t, err)
4691+
4692+
stdout, _, err := helpers.RunTerragruntCommandWithOutput(
4693+
t,
4694+
"terragrunt output -json --non-interactive --working-dir "+testPath,
4695+
)
4696+
require.NoError(t, err)
4697+
4698+
outputs := map[string]helpers.TerraformOutput{}
4699+
require.NoError(t, json.Unmarshal([]byte(stdout), &outputs))
4700+
4701+
assert.Nil(t, outputs["output1"].Value)
4702+
assert.Equal(t, "variable 2", outputs["output2"].Value)
4703+
}

0 commit comments

Comments
 (0)