-
-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Update terragrunt to generate tfvars.json file instead of using TF_VAR env var #1267
Conversation
|
||
for varName, varValue := range vars { | ||
envVarName := fmt.Sprintf("TF_VAR_%s", varName) | ||
variables, err := terraformModuleVariables(terragruntOptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sanity check: is it possible that we're somehow concealing a bug by only generating the .tfvars.json
file with the input vars we find?
I guess one possibility is that in terragrunt.hcl
, you have a typo: e.g., you set instance_typ = "t3.micro"
instead of instance_type = "t3.micro"
. Terragrunt wouldn't find an instance_typ
variable in your module's inputs and therefore wouldn't include it in the .tfvars.json
file. I guess this would be a bit confusing during debugging. That said, today, with env vars, you have the exact same problem, but it's less visible.
If we were to output a .tfvars
file instead of tfvars.json
, we could include a commented out section of variables that were in your inputs, but for which there were no matching input vars. That would make things a lot clearer...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is valid, but as you mentioned, we already have this problem, and unlike with env vars, you would spot that the instance_type
field is missing from the json when you load it. I think the benefit is marginal (e.g., it is just as easy to gloss over comments).
I agree that we should generate tfvars
, but in the interest of having something ship sooner, I think we should punt on this to the next iteration. Generating hcl
with comments in a robust manner will be complex since AFAIK, you can't just output the cty; you need to manipulate the AST to inject comment tokens.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roger. Could file issue to track generating a .tfvars
in future.
|
||
out[envVarName] = string(envVarValue) | ||
fileName := filepath.Join(terragruntOptions.WorkingDir, TerragruntTFVarsFileName) | ||
if err := ioutil.WriteFile(fileName, fileContents, os.FileMode(int(0600))); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we always override the previous file? Is it ever worth checking if the user has a file with such a name there already?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should always override it to avoid the element of surprise for now. I added a warning log when terragrunt detects the file: 6814a3a
When we convert to hcl, I think we should switch to the same behavior as overwrite_terragrunt
mode of generate
blocks, which is:
- Inject a signature as a comment to the generated file.
- If file does not exist, generate it.
- If file exists and has signature, overwrite it.
- If file exists but no signature, error out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roger
cli/args.go
Outdated
return out, nil | ||
terragruntOptions.Logger.Printf("Variables passed to terraform are located in \"%s\"", fileName) | ||
terragruntOptions.Logger.Printf("Run this command to replicate how terraform was invoked:") | ||
terragruntOptions.Logger.Printf("\tterraform apply \"%s\"", terragruntOptions.WorkingDir) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not always going to be terraform apply
, right? E.g., It could be plan
, destroy
, etc.
Moreover, due to extra_arguments
, we pass other args along too... Perhaps we should log the full, exact command we're executing more clearly for users?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Since we already log the terraform command with the args as part of RunCommandWithOutput
, and since I don't have access to the full args passed in at this stage, I decided to remove these logs and update the runcommand args to include the working dir.
} else if varIsInEnv { | ||
util.Debugf( | ||
terragruntOptions.Logger, | ||
"WARN: The variable %s was omitted from the debug file because the env var %s is already set.", | ||
varName, nameAsEnvVar, | ||
) | ||
} else if !varIsDefined { | ||
util.Debugf( | ||
terragruntOptions.Logger, | ||
"WARN: The variable %s was omitted because it is not defined in the terraform module.", | ||
varName, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we generated a .tfvars
instead of .tfvars.json
, we could include a commented-out section that has this exact info.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See above re:my thoughts on tfvars
hcl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work Yori 👍
cli/args.go
Outdated
// writeTFVarsFile will create a tfvars file that can be used to invoke the terraform module with the inputs generated | ||
// in terragrunt. | ||
func writeTFVarsFile(terragruntOptions *options.TerragruntOptions, terragruntConfig *config.TerragruntConfig) error { | ||
terragruntOptions.Logger.Printf("Generating tfvars file") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: specify what file is being generated (TerragruntTFVarsFileName
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done: f599a09
|
||
for varName, varValue := range vars { | ||
envVarName := fmt.Sprintf("TF_VAR_%s", varName) | ||
variables, err := terraformModuleVariables(terragruntOptions) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roger. Could file issue to track generating a .tfvars
in future.
fileName := filepath.Join(terragruntOptions.WorkingDir, TerragruntTFVarsFileName) | ||
|
||
// If the file already exists, log a warning indicating that we will overwrite it. | ||
if util.FileExists(fileName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this warning appear if you re-run a Terragrunt command and there's a previously generated file in there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately yes, as currently there is no way to distinguish between a file that is user created and a file that is terragrunt generated (since we don't have comments to inject a signature).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To reduce noise, should we only log it as a debug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point: done in c7c4b71
|
||
out[envVarName] = string(envVarValue) | ||
fileName := filepath.Join(terragruntOptions.WorkingDir, TerragruntTFVarsFileName) | ||
if err := ioutil.WriteFile(fileName, fileContents, os.FileMode(int(0600))); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Roger
I believe this is ready to merge now, but quick sanity check. When we first visited this idea, you had some reservations in #752 (comment). I have an answer to all but one of your questions (about secrets), which is worth thinking through prior to merging this in:
The file is named In future iterations we can better handle a user provided
No, so that it can be debugged. However, we should consider if that is the right approach given the next bullet:
**** I don't have a solution for this. Perhaps this is what forces us to delete this file after each run?
Based on the terraform resolution order, these args will merge into the generated inputs. That is, any var defined in I believe this was originally asked in the ticket because we were considering passing in the generated tfvars file with
Same as above bullet. |
Hm, yea, this is a tough one. Some options to think through:
|
I decided to go with option (2). Now the tfvars file is deleted with a
Looking at the docs for panic, it looks like all defer calls are executed normally (emphasis mine):
So I think as long as the defer is done before the call to |
Hm, I still a bit nervous about potentially writing secrets to disk:
Is it worth spending 30 min looking for a way on Windows to pass a file purely in memory? I know it can be done on *nix... |
I don't think this kind of testing is going to be very easy without introducing a potential avenue for bugs in terragrunt (I am nervous about injecting something in the pipeline to induce a panic intentionally)...
Ok will look into it, but it'll be a while before I'll have time available to set up my dev env on my windows boxes again. Let's then just revert back to the original debug mode version (option 3) for now so that we can ship something useful to satisfy this need of terragrunt debugging, and we don't block that on my ability to dedicate free nights and weekends to work on this. |
150b34b
to
a169425
Compare
UPDATE: rebased on #1263 so that this PR will automatically become an extension of that branch when that is merged, so that we don't throw away the work here. |
Roger |
…terraform with that instead of env vars
Co-authored-by: Yevgeniy Brikman <brikis98@users.noreply.github.com>
…ars file will happen frequently
a169425
to
533cdd4
Compare
Just wanted to drop a comment here thanking you for considering security in this case. If terragrunt started writing secrets to disk we'd have to immediately stop using it. We've stringent security policies in place that disallow secrets being written to plaintext on disk (even though the workstations/servers are full disk encrypted).... even for a few seconds. It is common for workstation backup software to run in a continuous mode that constantly backs up changes to disk as they happen. So writing secrets to disk could easily result in them being swept into a backup archive somewhere. That said, I'm definitely interested in the original motivation of this issue. |
To manage normal values vs secret values, could we maybe use two different inputs blocks, or a function? One input block would always write a tfvars file, the other would always export ENVs. Or check if that value is wrapped in the function, and exclude it from the tfvars and instead export it as an env... |
Closing as stale |
Fixes #752
Extends #1263 to always generate tfvars json file instead of relying on a debug mode.
NOTE: I decided to open a new PR on a new branch so that we have the other branch in case we decide the debug mode is better.