Outputs and Sensitive Values

Scenario: A database password variable is carefully marked sensitive = true, and the team concludes from that alone that the password can never show up in state or be retrieved by anyone with legitimate CLI access - which is not actually true.

An output block is how a module publishes the specific results it wants callers - a parent module, a human running the CLI, or an automation pipeline - to consume. Think of outputs as the flip side of the input-variable contract from earlier in this module: variables are what a module accepts, outputs are what it promises to hand back.

Marking an output sensitive = true changes how it's displayed: the normal CLI plan/apply summary shows (sensitive value) instead of the real value, and that sensitivity propagates through any expression that touches it. That's a real, useful protection against accidental screen-sharing or log-scraping - but it is strictly a display-time behavior. The underlying value is still written into Terraform state in full, and an authorized user running terraform output -raw bootstrap_token will still retrieve the real value on purpose - which is often exactly the intended, legitimate use (a CI pipeline reading a generated token to pass to the next step). nonsensitive() is the explicit, rarely-needed function for deliberately stripping the sensitive flag back off a value - treat every use of it as something that deserves its own review comment explaining why.

Analogy: A sensitive label is a privacy screen stuck onto a monitor in a busy open-plan office - it stops someone glancing over your shoulder from casually reading the number on screen. It does not encrypt the underlying record in the filing cabinet; anyone with a legitimate key to that cabinet (state access) can still open the file and read it in full.

A worked configuration

output "endpoint" {
  value       = fakecloud_server.api.address
  description = "Stable API endpoint for downstream configuration."
}
output "bootstrap_token" {
  value     = fakecloud_token.api.value
  sensitive = true
}

Because state is where the real protection has to live, encrypt remote state at rest and in transit, and restrict who can read state more tightly than who can merely read the configuration code (code review access and state access are very different privilege levels). Avoid outputting secrets at all where you can instead have the consumer fetch a short-lived credential directly from a dedicated secrets manager like Vault. Treat plan files, crash logs, and any CI artifact the same way you'd treat state itself - the same real value can leak through any of those channels regardless of the sensitive flag on the output.

Common mistakes in production

Goal: Never commit Terraform state, saved plan files, .tfvars files containing real secrets, or debug logs to version control. The sensitive flag is an interface display behavior, not a storage-level security boundary - it will not stop any of these files from containing the real, unredacted value. Reinforce this lesson in the tf-sensitive-outputs Terraform lab. Open /labs/terraform and choose slug tf-sensitive-outputs; the lab runs real Terraform against the offline FakeCloud provider.