TF Associate 003 — Sensitive Variables and Secrets in State — the flag and the file
What does sensitive = true protect? It protects the terminal.
Section IFrame
The exam asks it as a one-liner and the wrong answer feels right: what does sensitive = true protect?
It protects the terminal.
That is the whole answer, and everything else in this lesson follows from it. sensitive = true tells Terraform to print (sensitive value) where it would otherwise print the value. The value still travels to the provider. The value still lands in the state file, in the clear. The value still lands in the plan file, in the clear. One flag, one scope: what a human sees on a screen and what a CI job writes into a build log.
Brikman calls the state situation an open issue since 2014 with no clear plan for a first-class solution (Ch. 6, Plan files, pp. 341-343). The exam does not ask you to fix it. The exam asks whether you know it is true.
Call the pair the flag and the file. The flag governs display. The file governs exposure. Candidates who fuse them lose two questions and, later, a credential.
Section IISensitive Variables (Objective 8)
Input variables
variable "db_password" {
description = "Database password used by the application."
type = string
sensitive = true
}
Marking an input sensitive does four things. Terraform redacts it in plan and apply output. Terraform redacts any expression that derives from it, which is the propagation rule the exam likes. Terraform refuses to write it into an unmarked output. And the value still sits in terraform.tfstate as plain JSON.
Propagation is the part worth drilling. Take Lab 24's shape from the Terraform Pro set:
locals {
connection_string = format("postgres://%s:%[email protected]:5432/app",
var.db_username, var.db_password)
}
local.connection_string is now sensitive because one of its inputs was. Terraform tracks the taint through the expression graph. An output that publishes it must declare sensitive = true or the plan errors out before anything runs.
Five ways to supply the value, in Terraform's own precedence order, lowest to highest: the variable's default, the TF_VAR_ environment variable, a terraform.tfvars file, an *.auto.tfvars file, then -var and -var-file on the command line in the order given. The exam tests precedence. Operationally, only the environment variable and a secret store belong anywhere near a real password, because a .tfvars file holding a credential ends up committed by somebody eventually.
Outputs
output "connection_string" {
value = local.connection_string
sensitive = true
}
Brikman states the mechanism plainly in the chapter-two walkthrough: setting the parameter instructs Terraform not to log this output at plan or apply, which is useful when the output carries a password or private key (pp. 120-121).
Then the trap, straight out of Lab 24:
output "password_debug" {
value = nonsensitive(var.db_password)
}
nonsensitive() strips the marking. It exists for the legitimate case where an operator has satisfied themselves that a derived value carries no secret and the redaction is now hiding a diagnostic they need. Applied directly to a password, it is a deliberate publication. The function does exactly what it says and gives no warning, because the author asserted the value was safe by calling it.
nonsensitive() wrapping something that came from a sensitive = true variable is asking whether you noticed. The answer is always that the value is now printed.
The third place sensitivity lives
Providers mark attributes sensitive in their own schemas. azurerm_storage_account.primary_access_key, aws_db_instance.password, google_service_account_key.private_key: these arrive redacted whether or not any variable was marked. Terraform respects the provider's declaration. This is why a plan can print (sensitive value) for an attribute the configuration never marked.
Section IIISecrets in State and Plan (Objective 4)
What the state file holds
Every attribute of every managed resource, as returned by the provider, in plain JSON. Password fields included. The sensitive flag never touches this layer.
Three consequences the exam draws on:
- The state file must be treated as a secret. Remote backends that encrypt at rest and restrict access by IAM are the answer, and Brikman notes that most remote backends natively support encryption in transit and at rest and expose access controls (Ch. 3, pp. 150-151). Never in version control.
terraform_remote_statereads everything. A data source pointing at another stack's state gives the reader the whole file, not the outputs they asked for. Access to a state file is access to every secret inside it.terraform state pullandterraform show -jsonrender the same contents to a local terminal. The 08-03 lesson usedstate pullfor surgery. It is also the fastest way to print a database password to stdout.
What the plan file holds
The same secrets. terraform plan -out=tfplan writes the resolved values into the binary artifact, and Brikman's caution runs alongside the terraform apply example.plan workflow: the secrets pass into resources and data sources, so they are in there (Ch. 6, pp. 341-343).
This is the direct interlock with today's other two lessons. Both of them read the plan file, one through a policy engine and one through Python. A plan artifact uploaded to a build-artifact store, or left in the workspace for the next job, has handed the credential to every reader of that store.
The four ways to supply a secret, and what each costs
Brikman's chapter-six comparison closes on the working rule: for resources and data sources, use environment variables, encrypted files, or a secret store (pp. 343-345).
| Mechanism | Where the secret lives | Cost |
|---|---|---|
Environment variables (TF_VAR_db_password) |
The CI system's secret store | Free. Nothing durable to manage. Not everything is expressible; the CI system becomes the secret store by default. |
Encrypted files (SOPS, aws kms, sops + KMS key) |
Version control, encrypted | Works across providers. Key rotation is manual, and compromise of the key exposes every secret ever encrypted with it (pp. 328-329, pp. 334-337). |
| Secret store (Vault, AWS Secrets Manager, Azure Key Vault) | The store | Access auditing, rotation, fine-grained policy. Runs money, and inside-vs-outside the cloud provider decides which options remain (pp. 343-345). |
Hardcoded in .tf |
Version control, clear | Never. |
Brikman also separates the three kinds of secret at p. 308: personal secrets, customer secrets, and infrastructure secrets. Most tools store exactly one kind well. Forcing a tool to hold a kind it was not built for is a security problem rather than an inconvenience.
Section IVHCP Terraform Policy Enforcement (Objective 9)
The run pipeline
plan → cost estimation → policy check → apply
Policy runs after the plan because it reads the plan. Policy runs after cost estimation because a Sentinel rule can read the cost delta from tfrun and refuse on price.
Three enforcement levels
| Level | Effect on a failing run | Override |
|---|---|---|
advisory |
Logged, displayed, run continues | Not applicable |
soft-mandatory |
Run stops | A user with override permission on the workspace |
hard-mandatory |
Run stops | None. Change the code or change the policy. |
OPA policy sets on HCP Terraform carry advisory and mandatory only. A question naming three levels is asking about Sentinel.
Lab 25 of the Pro set frames the design question the exam only gestures at: map the producer-consumer workspace topology, then decide where policy checks should block applies, and choose plan-only versus apply permission boundaries. Those three decisions are one decision seen from three sides.
Variables in HCP Terraform
Workspace variables carry two flags the local workflow has no equivalent for. HCL marks the value as an expression rather than a string. Sensitive write-onlys the value: it can be set and replaced, never read back, not by the UI and not by the API.
Variable sets attach one collection of variables to many workspaces, which is how a cloud credential reaches forty workspaces without forty copies. Precedence runs from lowest to highest: variable set, then workspace variable, then command-line -var on an API-driven run. The workspace value beats the set. That ordering is exam material and it is the opposite of what people guess, because they reason from "the set is more central, so it must win."
Permissions as the third gate
Plan-only access and apply access are separate grants. A reviewer who can queue a plan and read its output, and cannot apply, is the human half of the same discipline the policy check automates. The 07-28 lesson took HCP Terraform on the provider and workspace side. This is the governance side.
Section VPractice Questions
variable block sets sensitive = true. After terraform apply, where can the value be read in plain text?-out. The flag redacts CLI and log output only. It changes nothing about storage.local, and the output block has no sensitive argument. What happens?terraform plan errors. Terraform propagates sensitivity through the expression graph, and refuses to publish a sensitive-derived value through an unmarked output. Add sensitive = true to the output, or use nonsensitive() if the derived value genuinely carries no secret.TF_VAR_region, a terraform.tfvars entry, or -var="region=eastus"?-var. Order lowest to highest: default, TF_VAR_, terraform.tfvars, *.auto.tfvars, then -var and -var-file in the order given on the line.terraform_remote_state so a downstream stack can read it. What is the exposure?soft-mandatory. A run fails the policy. Who can proceed, and what is the record?hard-mandatory admits no override; advisory never stops the run in the first place.-var on an API-driven run beats both.Section VIExam Notes and Traps
sensitive = trueon a variable with adefault. Legal, and the default is still visible in the configuration file to anyone reading the repository. The flag redacts output, not source.terraform outputfor a sensitive output. Prints<sensitive>.terraform output -jsonprints the value. The JSON form is machine-facing and does not redact. Candidates get this backwards.terraform console. Evaluates expressions against state and does not redact the way plan output does. Convenient, and a fast accidental disclosure.- Provider-marked attributes. A plan can show
(sensitive value)for something no variable marked. That is the provider schema, not the configuration. nonsensitive()on an unknown value. Errors. The function requires a value it can confirm is not sensitive at evaluation time.- Policy check ordering. After cost estimation, before apply. A question placing the policy check before the plan is wrong on its face, because there would be nothing to check.
Section VIIClosing
The flag governs the screen. The file governs the secret. Hold the two apart and six questions on this exam become one question you already answered.
Then carry the operational half into your own repositories. Put the state in a remote backend with encryption and access control, and treat every principal who can read that backend as a principal who knows every password inside it. Keep plan files out of build-artifact stores. Supply credentials through the environment or a secret store, and let the .tfvars file hold the region and the instance count and nothing that would matter if it appeared in a pull request.
Sit Lab 24 before the next TF day. Fix the outputs it breaks on purpose, then read the state file it produces and find the password anyway.
Examine well.
Related
- Prior arc: TF Associate 003 — State Manipulation and Refactoring (Objectives 4 & 7)
- Domain hub: Archmagus-Stack
- Grounding tome: Terraform: Up and Running, 3ed (Ch. 6, Managing Secrets with Terraform, pp. 308-345)
- Paired Ops lesson: Policy as Code for Terraform on Azure
- Paired Dev lesson: Python over the Terraform Plan JSON
Filed 2026-08-09 · Fajr trio #84 · sprint day 18 · TF track
Paired: 01-Earth-DevOps/Synthesis-Lessons/2026-08-09-policy-as-code-for-terraform-on-azure/ · Polyglot-Dev/Python/2026-08-09-python-over-the-terraform-plan-json/