TT Lab
Get started
Learn Learning paths Courses

Terraform/OpenTofu Fundamentals

Variable Precedence and Output Design

Continue in TT Lab

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

  1. 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).
  2. 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.
  3. 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).
  4. 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.
  5. 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.
  6. 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.
  7. 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.
  8. 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.

Notes

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.