Skip to main content
RunBook Academy

TerraformXIII · Variables, Outputs, and LocalsProduction Terraform

Sensitive Values and the Production Interface

Intermediate⏱ ~12 minbash

What you'll learn

  • Mark variables, outputs, and resource attributes sensitive when warranted
  • Predict what `sensitive = true` hides and what it does not
  • Source secrets from a secrets manager via a `data` source, not from tfvars
  • Audit a configuration for surfaces that still expose sensitive values
  • Rotate secrets after any incident involving exposure

Prerequisites

None — start here.

Verified against Terraform CLI 1.9.x · OpenTofu 1.7.x · HCL 2.0 · bpg/proxmox provider 0.66+ · hashicorp/local provider 2.5+ · hashicorp/null provider 3.2+ · hashicorp/random provider 3.6+ · hashicorp/http provider 3.4+ · Ubuntu 24.04 LTS · Debian 12 (Bookworm) · 2026-08-13

Not yet marked complete on this device.

A database password sits in a Terraform variable. The operator types terraform apply and the password scrolls past in the plan output, into the CI log, into the Slack alert, into the engineer’s terminal scrollback. The sensitive = true flag is the smallest defensive layer that prevents that scroll. It is not encryption. It is not access control. It is the difference between a leak you can trace and a leak that goes everywhere.

A real production incident: the team marked db_password as sensitive but used it as the password argument to aws_db_instance. The apply succeeded. A week later, an engineer ran terraform state pull to debug a different issue and the password appeared in the engineer’s terminal. The flag suppressed the CLI output; it did nothing to the state file.

What sensitive = true hides

The flag is a UI feature. It tells the Terraform CLI and the UI to redact the value wherever it would otherwise be rendered.

# CONFIGURATION

variable "db_password" {
  type        = string
  sensitive   = true
  description = "Master password for the production RDS instance."
}

resource "aws_db_instance" "prod" {
  engine         = "postgres"
  engine_version = "16.3"
  username       = "admin"
  password       = var.db_password

  tags = {
    Environment = "production"
  }
}

Plan output (READ-ONLY):

# aws_db_instance.prod will be created
  ...
  password              = (sensitive value)
  username              = "admin"
  ...
  Plan: 1 to add, 0 to change, 0 to destroy.

The (sensitive value) placeholder appears in:

  • terraform plan output
  • terraform apply output (after the change is applied)
  • terraform output for sensitive outputs
  • terraform console when the value would be displayed
  • The UI of Terraform Cloud / Enterprise (where supported)

What sensitive = true does NOT hide

The flag is a UI redactor. It is not encryption. It is not access control. The value persists in plaintext in several places:

SurfacePlaintext?Why
State fileyesState records the actual value for drift detection
Cloud provider’s view of the resourceyesThe password is set on the database
local-exec / remote-exec stdoutyesThe provisioner writes whatever it writes
terraform state pullyesDumps the raw state
terraform console var.db_passwordyesThe console evaluates the expression
Other tools that touch the stateyesAnyone with read access to state
# READ-ONLY — proves the value is in state
terraform show -json | jq '.resources[]
  | select(.type == "aws_db_instance")
  | .instances[].attributes.password'
"the-actual-password-in-plaintext"

Source secrets from a secrets manager

The discipline is to never put a secret in the configuration in the first place. Fetch it at apply time from a manager you control.

# CONFIGURATION

data "aws_secretsmanager_secret_version" "db_password" {
  secret_id = "production/db/master"
}

resource "aws_db_instance" "prod" {
  username = "admin"
  password = data.aws_secretsmanager_secret_version.db_password.secret_string
  # ...
}

output "db_endpoint" {
  value     = aws_db_instance.prod.endpoint
  sensitive = true
}

The data source does not log the value. The apply output redacts it because the resource attribute that consumes it is sensitive (the AWS provider marks password sensitive automatically). The state file contains the value; the encryption-at-rest of the state backend is the real control.

WhyThisMatters

WhyThisMatters A sensitive flag on a variable is the smallest defensive layer. The real production pattern is: secrets manager for storage, IAM for access, encryption-at-rest for state, and rotation as a runbook. The flag is the difference between a leak that goes to a terminal scrollback and a leak that is logged in three systems.

Failure modes

  1. local-exec writes the value. A provisioner logs var.db_password to a file. The sensitive flag does nothing — the value is in the file in plaintext.

  2. Sensitive variable feeds a non-sensitive output. The output propagates the value into another stack that does not mark the input sensitive. The downstream plan renders the value.

  3. State file in a public S3 bucket. Anyone with the URL reads terraform.tfstate and decrypts nothing — the values are plaintext.

  4. terraform console reads the variable. The CLI prints the value to the operator’s terminal. The flag is bypassed.

  5. Module passes sensitive to non-sensitive child input. The child module’s plan renders the value unless the child input is also marked sensitive.

  6. -var-file with a sensitive value committed to Git. The file lands in the repository. The flag does not retroactively un-commit the value.

  7. terraform output -json piped to a logger. The JSON contains (sensitive value) as a marker, but -raw mode on a sensitive output renders the value. A script that pipes -raw to a log leaks.

Audit checklist

When a sensitive value is in scope, the audit answers five questions:

  1. Source. Where does the value come from? If .tfvars in Git, the audit fails immediately.
  2. Storage. Is the state file encrypted at rest? Who can read it?
  3. Propagation. Does the value flow into a non-sensitive downstream input? Mark every consumer sensitive.
  4. Provisioners. Does any local-exec or remote-exec write the value to a file, log, or API call? Remove the write or mask the value.
  5. Rotation. Is the secret in a rotation runbook? After any exposure, the answer is “rotate now.”

Production guidance

  • Mark variables sensitive when they hold secrets. Do not mark variables sensitive “just in case” — the flag suppresses useful plan output and adds friction to debugging.
  • Mark outputs sensitive when they expose the same secrets (database endpoints, API keys).
  • Never store secrets in *.tfvars, terraform.tfvars, or -var CLI flags. The CI log is a public surface.
  • Read secrets via data sources from a secrets manager you control.
  • Encrypt the state file at rest. Lock the state backend to the smallest possible set of IAM principals.
  • After any incident involving exposure, rotate the secret. The flag does not make an exposed secret safe.

What comes next

The next lesson is Outputs: The Configuration Interface — the contract by which one Terraform stack hands values to another, and the sensitivity discipline that applies across stacks.

Verification

  1. A variable is marked sensitive = true. Does the state file contain the value in plaintext? Why?
  2. A local-exec provisioner writes var.db_password to a file. What does the sensitive flag do in this case?
  3. You mark a variable sensitive but feed it into a non-sensitive downstream module input. What happens in the downstream plan?
  4. Where in the production pipeline is the password stored when you use data "aws_secretsmanager_secret_version"?

Knowledge check · 7 questions

  1. Q1. What does `sensitive = true` on a variable do?

  2. Q2. The Terraform state file stores sensitive variables in encrypted form.

  3. Q3. Where should production secrets such as database passwords come from?

  4. Q4. A `local-exec` provisioner writes `var.db_password` to a log file. What does `sensitive = true` do in this case?

  5. Q5. Which of the following surfaces still expose a sensitive value despite the `sensitive = true` flag? (Select all that apply.)

  6. Q6. Which command can bypass the `sensitive` suppression on a variable?

  7. Q7. An auditor asks where the production database password lives. Which answer demonstrates a defensible posture?

Passing score: 75%. Answers are checked in this browser.