@@ -5,6 +5,7 @@ package run
55
66import (
77 "context"
8+ "encoding/json"
89 "fmt"
910 "io"
1011 "maps"
@@ -35,6 +36,7 @@ import (
3536
3637const (
3738 CommandNameTerragruntReadConfig = "terragrunt-read-config"
39+ NullTFVarsFile = ".terragrunt-null-vars.auto.tfvars.json"
3840)
3941
4042var 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+ }
0 commit comments