Terraform/OpenTofu Fundamentals
Variable Precedence and Output Design
Goal
You put a value into the same variable in four ways to confirm the precedence directly, and complete a configuration with validation, locals, outputs, and sensitive values.
Why it matters
The answer to the question "I changed the variable but it isn't reflected" is almost always precedence. The tool accepts values of the same name from several places, and even when they conflict it does not raise an error but quietly uses the stronger one. The more convenient it is, the harder the debugging, so you need a sense of putting values meant to be overridden in weak places and values that must be kept in strong places. In particular, a value passed on the command line beats anything but is recorded nowhere, so if you use it in incident response and do not reflect it in the code, the next deployment silently reverts it. Finally, sensitive = true only hides the screen output, and the value remains as it is in the state file and the JSON output. Believing "I marked it sensitive, so it's safe" without knowing this limit is the start of the most common accident.
Steps
- In
/root/tf/vars/variables.tf, declare anenvironmentvariable. It must have all oftype = string,default = "dev", anddescription. In/root/tf/vars/outputs.tf, create an output namedenvironment, apply with the default as it is, and then save the output as JSON to/root/tf/vars/out/default-outputs.json(.environment.valueisdev). - In
/root/tf/vars/terraform.tfvars, writeenvironment = "stage", apply again, and save the output to/root/tf/vars/out/tfvars-outputs.json. The value must change tostage— the file beats the default. - This time, give the value on the command line with
-var environment=prod, apply, and save the output to/root/tf/vars/out/cli-outputs.json.tfvars-outputs.jsonmust be left as it is (stillstage). - In
variables.tf, declare anownervariable, but do not hardcodeplatformas the default. Pass the value with the environment variableTF_VAR_owner=platform, apply, and save the output to/root/tf/vars/out/env-outputs.json(.owner.valueisplatform). Also, inoutputs.tf, add anowneroutput. - In
variables.tf, add areplica_countvariable and put in avalidationblock that allows only 1 to 5 inclusive. Bothconditionanderror_messagemust be present, and insideerror_messagethe allowed range must appear literally as the text1-5. Run with an out-of-range value (for example,-var replica_count=9) and save the rejected output to/root/tf/vars/out/validation.txt. - In
/root/tf/vars/main.tf, create alocalsblock and definename_prefixas the value made by joiningenvironmentandownerwith a hyphen. Tooutputs.tf, add aname_prefixoutput. In thecli-outputs.jsonthat you will remake in step 8, thename_prefixmust beprod-platform. - Gather the outputs in one place,
/root/tf/vars/outputs.tf, and have at least four (environment,owner,name_prefix,config_path), but attach adescriptionto every output. Forconfig_path, in thelocal_filethat this configuration creates, export itsfilenameas it is. - In
variables.tf, declare anapi_tokenvariable withsensitive = trueand pass the value withTF_VAR_api_token. Also, inoutputs.tf, add anapi_tokenoutput withsensitive = true. Finally, give-var environment=prod,TF_VAR_owner=platform, andTF_VAR_api_tokentogether and apply, then recreatecli-outputs.json, and also save the output in the human-readable format to/root/tf/vars/out/sensitive.txt. The latter must show a masked marker instead of the value, and the actual token string must not be visible.
Notes
- The strength of the values, from lowest, is
default→TF_VAR_*→terraform.tfvars→*.auto.tfvars→-var-file→-var. terraform output -jsonis the machine-readable format andterraform outputis the human-readable format. The sensitive marking hides the value only in the latter.cli-outputs.jsonis the file that the grading for steps 6, 7, and 8 all look into. Each time you add an output, be sure to recreate it at the end with the value combination of step 8.- Common mistake 1: saving to the same file at every step and losing the earlier step's result. Steps 1–4 must each be left in a different file.
- Common mistake 2: writing the default of
ownerasplatform. Then you cannot prove that the environment variable worked, and grading rejects it.
Declare a variable with a type, default, and description
In /root/tf/vars/variables.tf, declare an environment variable. It must have all of type = string, default = "dev", and description. In /root/tf/vars/outputs.tf, create an output named environment, apply with the default as it is, and then save the output as JSON to /root/tf/vars/out/default-outputs.json (.environment.value is dev).
A variable block has things to attach besides the value. Without all three, in a few months nobody knows what this value is. Extract the output with -json and save it.
Override the default with a tfvars file
In /root/tf/vars/terraform.tfvars, write environment = "stage", apply again, and save the output to /root/tf/vars/out/tfvars-outputs.json. The value must change to stage — the file beats the default.
A file with a particular name inside the directory is read automatically without your specifying it. Inside the file, use only the form 이름 = 값 (name = value).
Beat the file with a command-line value
This time, give the value on the command line with -var environment=prod, apply, and save the output to /root/tf/vars/out/cli-outputs.json. tfvars-outputs.json must be left as it is (still stage).
At the top of the precedence is the value given directly at run time. Do not overwrite the output file you saved in the earlier step; leave it in a new file.
Inject a value with an environment variable
In variables.tf, declare an owner variable, but do not hardcode platform as the default. Pass the value with the environment variable TF_VAR_owner=platform, apply, and save the output to /root/tf/vars/out/env-outputs.json (.owner.value is platform). Also, in outputs.tf, add an owner output.
The tool reads on its own an environment variable made by attaching a fixed prefix to the variable name. If you hardcode the same value in the default, you cannot prove that the environment variable worked.
Pin the allowed range in code
In variables.tf, add a replica_count variable and put in a validation block that allows only 1 to 5 inclusive. Both condition and error_message must be present, and inside error_message the allowed range must appear literally as the text 1-5. Run with an out-of-range value (for example, -var replica_count=9) and save the rejected output to /root/tf/vars/out/validation.txt.
Inside the variable block, put a block that holds the condition and the message. The message must let the user fix things by seeing only it, so write the allowed range literally.
Combine a name prefix with locals
In /root/tf/vars/main.tf, create a locals block and define name_prefix as the value made by joining environment and owner with a hyphen. To outputs.tf, add a name_prefix output. In the cli-outputs.json that you will remake in step 8, the name_prefix must be prod-platform.
locals is a named expression that cannot be injected from outside. Make a value by joining the two variables with a hyphen and export it as an output.
Gather the outputs with descriptions
Gather the outputs in one place, /root/tf/vars/outputs.tf, and have at least four (environment, owner, name_prefix, config_path), but attach a description to every output. For config_path, in the local_file that this configuration creates, export its filename as it is.
Outputs are this configuration's public interface, so gather them in one file. If even one is missing a description, grading counts and catches it.
Mask a sensitive value and confirm its limit
In variables.tf, declare an api_token variable with sensitive = true and pass the value with TF_VAR_api_token. Also, in outputs.tf, add an api_token output with sensitive = true. Finally, give -var environment=prod, TF_VAR_owner=platform, and TF_VAR_api_token together and apply, then recreate cli-outputs.json, and also save the output in the human-readable format to /root/tf/vars/out/sensitive.txt. The latter must show a masked marker instead of the value, and the actual token string must not be visible.
The sensitive marking is needed on both the variable and the output. Save the output a person sees and the output a machine reads separately, and compare how the two results differ.