Fix Terraform 'Output refers to sensitive value' Error When Exporting Secrets

intermediate๐Ÿ—๏ธ Terraform2026-04-02| Terraform 0.14+, any OS (Linux, macOS, Windows), any cloud provider

Error Message

Error: Output refers to sensitive value; use 'sensitive = true' to allow this output
#terraform#output#sensitive#security

The Error

You write an output block that references a sensitive resource attribute โ€” a database password, an API key, a generated secret โ€” and terraform plan or terraform apply immediately stops with:

Error: Output refers to sensitive value

  on outputs.tf line 3, in output "db_password":
   3:   value = aws_db_instance.main.password

To allow this, the output must be marked with sensitive = true.
To expose the value, set the "sensitive" argument.

Terraform sees you're about to surface a secret through an output block. It won't proceed until you make a deliberate call about what to do with it.

Why This Happens

Terraform 0.14 shipped automatic sensitivity tracking. Providers mark certain attributes as sensitive in their schema โ€” aws_db_instance.password, random_password.result, and friends. That sensitivity flag propagates through your config like a tag.

Once a sensitive value reaches an output block, Terraform requires you to acknowledge it explicitly. You either mark the output sensitive, or you transform/redact the value on purpose. Silence isn't an option.

Common sources that trigger this:

  • aws_db_instance.*.password
  • random_password.*.result
  • aws_secretsmanager_secret_version.*.secret_string
  • azurerm_key_vault_secret.*.value
  • Any variable declared with sensitive = true

Fix 1 โ€” Mark the Output as Sensitive (Most Common)

Another module needs this value? A CI/CD pipeline pulls it from state? Add sensitive = true to the output block:

# outputs.tf
output "db_password" {
  value     = aws_db_instance.main.password
  sensitive = true
}

Terraform still writes the value to state. It just prints (sensitive value) in the terminal instead of the actual secret. Your pipelines can still read it:

terraform output -raw db_password

That command prints the raw value to stdout โ€” useful for feeding secrets into scripts without ever displaying them in Terraform's own output.

Fix 2 โ€” Don't Output the Secret at All

Honestly, the most robust solution is to stop exporting secrets from Terraform entirely. Output the ARN or path instead, and let the consuming service fetch the secret at runtime.

# Output the reference, not the secret itself
output "db_secret_arn" {
  value = aws_secretsmanager_secret.db.arn
}

# Your app retrieves the secret at runtime using the ARN
# Nothing sensitive ever flows through Terraform output

This pattern keeps secrets out of CI logs, Terraform output, and anywhere humans might accidentally read them. For production environments, it's the approach worth aiming for.

Fix 3 โ€” Use nonsensitive() to Explicitly Unsensitize

Sometimes you genuinely need the plaintext value in output โ€” a throwaway dev environment, a value that's already public, a migration where you've accepted the tradeoff. The nonsensitive() function strips the sensitivity flag:

# outputs.tf โ€” only when you truly intend to expose the value
output "db_password" {
  value = nonsensitive(aws_db_instance.main.password)
}

No confirmation dialog. Terraform trusts you. The value shows up in plaintext in your terminal and gets stored unredacted in state โ€” which is exactly why you should use this function sparingly.

Before reaching for nonsensitive(), ask: does this state file get stored in an S3 bucket that five engineers have access to? Could this value show up in CI logs? If the answer to either is yes, reconsider.

Fix 4 โ€” Sensitive Variables Passed to Outputs

When the sensitivity originates from an input variable you declared with sensitive = true, the same contract applies. Mark your output sensitive to honor it:

# variables.tf
variable "api_key" {
  type      = string
  sensitive = true
}

# outputs.tf
output "configured_api_key" {
  value     = var.api_key
  sensitive = true   # required โ€” source is sensitive
}

Fix 5 โ€” Sensitive Values Inside Complex Types

Sometimes the secret is buried inside a map or object alongside non-sensitive fields. Two options here.

Mark the whole output sensitive:

output "db_config" {
  value = {
    host     = aws_db_instance.main.address
    port     = aws_db_instance.main.port
    password = aws_db_instance.main.password
  }
  sensitive = true
}

Or split it โ€” keep the non-sensitive fields in a separate output and skip the password entirely:

output "db_connection_info" {
  value = {
    host = aws_db_instance.main.address
    port = aws_db_instance.main.port
    # password omitted โ€” consumers fetch it from Secrets Manager
  }
}

The second approach is cleaner when callers only need connection details, not credentials.

Verification

Run terraform plan โ€” no errors means the fix is in place. Then apply:

terraform apply

A sensitive output shows up in apply results like this:

Outputs:

db_password = <sensitive>

To confirm the value is actually stored and accessible when you need it:

# List all outputs (sensitive ones show as "<sensitive>")
terraform output

# Read a specific sensitive output โ€” prints raw value to stdout
terraform output -raw db_password

# Read everything as JSON (sensitive values are included)
terraform output -json

In a CI pipeline, capture it like this:

DB_PASS=$(terraform output -raw db_password)
# $DB_PASS is available to your script without appearing in Terraform's logs

Sensitive Values in Remote State

Reading your outputs from another config via terraform_remote_state? Sensitivity travels with the value across state boundaries:

data "terraform_remote_state" "db" {
  backend = "s3"
  config = {
    bucket = "my-tfstate"
    key    = "db/terraform.tfstate"
    region = "us-east-1"
  }
}

# Still treated as sensitive in this config โ€” no re-marking needed
locals {
  db_pass = data.terraform_remote_state.db.outputs.db_password
}

No need to re-annotate it. Terraform tracks it automatically.

Quick Decision Guide

  • Another module or pipeline needs the actual value? โ†’ sensitive = true
  • Consuming service can fetch the secret itself? โ†’ output the ARN or path, skip the secret
  • Non-prod, throwaway, or genuinely public value? โ†’ nonsensitive(), eyes open
  • Object with mixed sensitive and non-sensitive fields? โ†’ split into two outputs or mark the whole block sensitive

Related Error Notes