skip to content

In Terraform, what does marking an `output` block with `sensitive = true` actually hide, and what does it not protect?

level: middleimportance: must knowfreq 68%

answer

  1. a display flag, not a storage flag
  2. keeps CI job logs clean
  3. the mark spreads through expressions
  4. plain text in state regardless
  5. -json and -raw print it anyway

basics

~20 s

It redacts the value from Terraform's own display — plan, apply, and terraform output show (sensitive value) — and marks anything derived from it sensitive too. It is not encryption: the value is stored in plain text in state and printed by terraform output -json.

solid answer

~50 s

`sensitive = true` is a **display** control. Terraform replaces the value with `(sensitive value)` wherever it would normally render it: the plan diff, the apply summary, and plain `terraform output`. The mark is also contagious — any expression built from a sensitive value is itself sensitive, so it cannot leak by being concatenated into a string or wrapped in a map. That is genuinely useful, because the usual leak path is a CI job log that anyone with repo access can read. What it does **not** do is protect the value anywhere else: it is written to the state file in clear text (securing state is its own problem — encrypted remote backend, restricted access), `terraform output -json` and `terraform output -raw` print it in clear text by design, and a provider may still log it. Treat it as "don't print this", not "this is now a secret".

code

hcl · 16 lines
hcl
resource "random_password" "db" {
  length  = 32
  special = true
}

# random_password.result is sensitive in the provider schema,
# so this output MUST declare sensitive = true or the plan fails.
output "db_password" {
  value     = random_password.db.result
  sensitive = true
}

# Plan/apply render: db_password = (sensitive value)
# terraform output        -> <sensitive>
# terraform output -json  -> the real value, "sensitive": true
# state file              -> the real value, in plain text

go deeper

for a junior

Be able to add sensitive = true to an output and say what changes: Terraform prints (sensitive value) instead of the real one in plan, apply, and terraform output.

for a middle

Explain both halves — redaction plus contagion through derived expressions — and state plainly that the value is still in state in clear text and still printed by terraform output -json.

for a senior

Show you know which leak path each control closes: sensitive marks close the log path, an encrypted and access-controlled backend closes the state path, and a pipeline that runs terraform output -json reopens the first one.

for a principal

Argue where secrets should live at all. Decide whether Terraform should generate and hold credentials or only provision a secret-store entry the application reads at runtime, and set that as an organisation-wide default with policy checks behind it.

## The one-line mental model `sensitive = true` on an output tells Terraform *do not render this value on my screen or in my logs*. It is a redaction flag on the presentation layer. Every conclusion below follows from taking that literally. ```hcl output "db_password" { value = random_password.db.result sensitive = true } ``` ## What it does hide **The plan and apply output.** Wherever Terraform would print the value — the diff for a changed output, the `Outputs:` block after apply — it prints `(sensitive value)` instead. This is the leak path that matters in practice, because plan and apply output is routinely pasted into pull requests and archived in CI job logs that are readable by everyone with access to the repository, often long after the secret was rotated. **Plain `terraform output`.** Reading outputs back from state renders sensitive ones as `<sensitive>` rather than the value. **Anything derived from it.** Sensitivity is *contagious* through expressions. If a sensitive value is interpolated into a string, put in a list, used as a map value, or passed through most functions, the result carries the sensitive mark too. This is what stops the naive workaround — building `"postgres://user:${var.password}@host"` and exporting that — from leaking anything. Terraform notices and, for an output, refuses to plan unless you mark the output sensitive as well. **Propagation across the module boundary.** If a child module's output is sensitive, the value stays sensitive in the caller that consumes it, so a parent cannot accidentally un-redact a child's secret by re-exporting it. ## What it does not do **It does not encrypt or omit anything from state.** The value is written to the state file in plain text with a flag saying it is sensitive. Anyone who can read the state can read the secret — which is why state protection (encryption at rest on the backend, tight bucket policy, no state file in git) is a separate and mandatory control. That is a topic of its own; the point here is simply that `sensitive = true` is not a substitute for it. **It does not stop deliberate retrieval.** `terraform output -json` and `terraform output -raw NAME` print sensitive values in clear text on purpose, because scripts need them. The JSON form marks the field with `"sensitive": true` next to the value — metadata, not redaction. So a CI step doing `terraform output -json > outputs.json` re-exposes everything the plan carefully hid. **It does not control providers or provisioners.** A provider that logs a request body, or a `local-exec` command that echoes an argument, prints whatever it was handed. `TF_LOG=DEBUG` likewise dumps request payloads. **It does not make the value safe to hold.** The secret still transits the machine running Terraform and lands in whatever the configuration writes it into. ## Where the mark comes from You do not always add it yourself. Providers mark inherently secret attributes sensitive in their schema — `random_password.result`, an RDS instance's `password` argument — and an input variable can be declared `sensitive = true`. Because sensitivity is contagious, those marks propagate outward through your configuration on their own, which is why an output you never thought of as secret can suddenly demand `sensitive = true`. ## Reading it in a review The useful review questions are: is the output redacted where it will be displayed; is the state backend encrypted and access-controlled; does any pipeline step run `terraform output -json`; and — the best answer — does this secret need to leave Terraform at all, or can the consumer read it from a secret store directly. Terraform 1.10 also added `ephemeral = true` for values that must not be persisted at all; an ephemeral output is never written to state and can only be consumed by a parent module, never at the root. ## The interview answer in one breath It redacts from Terraform's own display and propagates that redaction through derived expressions, so plan and apply logs stay clean. It is not encryption, not state protection, and not a barrier to `-json` or `-raw`.

  • If sensitive = true does not protect state, what does?
    Backend-level controls: encryption at rest on the remote backend, a tightly scoped access policy so only the pipeline role can read the state object, versioning for recovery, and never committing state to git. Better still, avoid putting the secret in state at all — have Terraform create only a reference and let the consumer read the value from a secret store at runtime.
  • Does marking a child module's output sensitive protect it in the parent?
    Yes. The mark travels with the value, so the parent's plan output redacts it too and any expression the parent builds from it inherits sensitivity. The parent cannot un-redact it by re-exporting; it would have to call `nonsensitive()` explicitly, which is a visible, reviewable act rather than an accident.
  • Can an input variable be marked sensitive, and does that change output behaviour?
    Yes — `sensitive = true` on a `variable` block redacts it in plan output, and because sensitivity is contagious, anything derived from that variable becomes sensitive too. An output whose value traces back to it must then be marked sensitive as well or the plan fails with an explicit error.

saying these in an interview costs you the question

  • Says sensitive = true encrypts the value in state
  • Thinks a sensitive value is omitted from the state file
  • Assumes terraform output -json also redacts sensitive values
  • Believes wrapping a secret in a string hides it from Terraform
  • Treats sensitive outputs as a substitute for a secret manager

context