RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
Terraform Sensitive Output Flag Validator
02. Root Cause Vector

Terraform blocks plan/apply when a sensitive output lacks `sensitive = true`, risking secret leakage in state files, CI logs, and console...

03. Rapid Prescription

Refresh remote state lock, ensure provider version constraints, and audit state drift.

Zero-Trust Client IDE/ Security

Terraform Sensitive Output Flag Validator

Terraform blocks plan/apply when a sensitive output lacks `sensitive = true`, risking secret leakage in state files, CI logs, and console output.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(51L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# TODO: This module is a mess, but it's what we inherited from the PCI migration last year.
# The sensitive = true flag was added to some outputs but not others due to a merge conflict.
# The legacy-api-service module below is from our old on-prem stack, now containerized.

module "legacy_api_service" {
  source = "my-registry.internal/legacy-api:v2.1.3"

  # These are dummy secrets for testing; rotate in prod
  stripe_api_key = "sk_live_51KjFp8Lm9J8xYtN7vBcXzQaR3eT6yU9iP0oA2sD4fG5hJ6kL7mN8bV9cX0zQwErTyUiOp"
  aws_access_key = "AKIAIOSFODNN7EXAMPLE"
  aws_secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"

  # Multi-line RSA private key for SSH tunneling (dummy)
  ssh_private_key = <<-EOT
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArX8rJzJvL8K9KjHqFwLmNpOqRsTuvWxYyZabcdefghijklmn
opqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ+/==
-----END RSA PRIVATE KEY-----
EOT

  # Database URI with embedded credentials
  postgres_uri = "postgresql://admin:dummySecurePass123!@pci-secure-zone-db.internal:5432/legacy_pci_data"
}

# Outputs section - some are marked sensitive, others not, due to rushed deployment
output "stripe_key" {
  value = module.legacy_api_service.stripe_api_key
  # VULNERABILITY: Missing sensitive = true flag on a secret output, causing Terraform to error
}

output "aws_access_key_id" {
  value = module.legacy_api_service.aws_access_key
  sensitive = true
}

output "db_connection_string" {
  value = module.legacy_api_service.postgres_uri
  # TODO: Add sensitive flag after compliance review
}

# Red herring: commented-out old output that used to work
# output "ssh_key" {
#   value = module.legacy_api_service.ssh_private_key
#   sensitive = true
# }

# This output references a sensitive value but lacks the flag, triggering the target error implicitly
output "combined_secret" {
  value = "${module.legacy_api_service.stripe_api_key}:${module.legacy_api_service.aws_secret_key}"
  # VULNERABILITY: Output refers to sensitive value without sensitive = true, causing Terraform validation failure
}
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix Terraform 'Cannot Output Sensitive Value Without sensitive = true' Error

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5 mins

TL;DR

  • What broke: A Terraform output block references a value marked sensitive (e.g., from random_password, aws_secretsmanager_secret_version, or a variable with sensitive = true) but the output block itself is missing the sensitive = true attribute — Terraform refuses to proceed.
  • How to fix it: Add sensitive = true to every output block that surfaces a sensitive resource attribute.
  • Action: Use our Client-Side Sandbox above to auto-refactor this — paste your outputs.tf and get corrected HCL instantly without leaking values to a third-party server.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

Raw error from terraform plan or terraform apply:

╷
│ Error: Output refers to sensitive values
│
│   on outputs.tf line 3, in output "db_password":
│    3:   value = aws_db_instance.main.password
│
│ To reduce the risk of accidentally exporting sensitive data that was not
│ intended to be exported, Terraform requires that any root module output
│ containing sensitive data be explicitly marked as sensitive, to confirm
│ your intent.
│
│ If you do intend to export this data, annotate the output value as sensitive
│ by adding `sensitive = true` to the output block.
╵

Terraform tracks a sensitivity taint on values. When any upstream resource attribute is flagged sensitive (built-in to providers like aws_db_instance.password, random_password.result, or any var declared with sensitive = true), that taint propagates through expressions. An output block consuming a tainted value must explicitly acknowledge it. Without sensitive = true, Terraform hard-stops — it will not plan, apply, or generate a plan file.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

This isn't bureaucratic pedantry. The enforcement exists because Terraform state and CI output are primary secret exfiltration vectors:

1. Terraform State File (terraform.tfstate) Output values are stored in plaintext in state. If your backend is an S3 bucket without strict bucket policies, or a local file committed to git, every output value is readable by anyone with state access. Sensitive outputs are still stored in state, but the sensitive = true flag signals downstream tooling (Atlantis, Spacelift, TFC) to redact the value from logs and plan UI.

2. CI/CD Log Leakage Without the flag, terraform output db_password prints the raw value to stdout. In GitHub Actions, GitLab CI, or Jenkins, this lands in build logs — often retained for 90+ days, accessible to every developer with repo read access.

3. Lateral Movement via Exposed Credentials A leaked RDS password or API token in a CI log is a direct lateral movement path. Attackers scraping exposed CI logs (a documented technique) can pivot from a compromised developer account to production databases within minutes.

4. Blast Radius A single unguarded output block for a master DB password can expose: the database itself, any service using that shared credential, and potentially the entire VPC if the credential is reused.


How to Fix It

Basic Fix

Add sensitive = true to the offending output block.

 output "db_password" {
   description = "RDS master password"
   value       = aws_db_instance.main.password
+  sensitive   = true
 }

After this change, terraform plan and terraform output will display (sensitive value) instead of the raw string.


Enterprise Best Practice

Marking an output sensitive is the minimum. In a hardened pipeline, you should also:

1. Never output raw secrets at the root module level. Consume them directly in dependent resources.

- output "db_connection_string" {
-   value     = "postgresql://${var.db_user}:${aws_db_instance.main.password}@${aws_db_instance.main.endpoint}/prod"
-   sensitive = true
- }
+
+ # Preferred: write the connection string directly to AWS Secrets Manager
+ # and output only the secret ARN — never the value itself.
+ resource "aws_secretsmanager_secret_version" "db_conn" {
+   secret_id     = aws_secretsmanager_secret.db_conn.id
+   secret_string = jsonencode({
+     url      = "postgresql://${var.db_user}:${aws_db_instance.main.password}@${aws_db_instance.main.endpoint}/prod"
+   })
+ }
+
+ output "db_secret_arn" {
+   description = "ARN of the Secrets Manager secret containing DB credentials"
+   value       = aws_secretsmanager_secret.db_conn.arn
+   # ARN is not sensitive — the secret value never leaves AWS.
+ }

2. Use remote state with encryption and strict IAM.

 terraform {
   backend "s3" {
     bucket         = "my-tfstate-bucket"
     key            = "prod/terraform.tfstate"
     region         = "us-east-1"
+    encrypt        = true
+    kms_key_id     = "arn:aws:kms:us-east-1:123456789012:key/your-kms-key-id"
   }
 }

3. Audit all outputs in a module tree. Run this before every PR merge:

grep -rn 'output' . --include='*.tf' | grep -v 'sensitive = true'

Any output referencing a password, token, key, or secret that appears in this list is a finding.


💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

1. Checkov — catches missing sensitive = true pre-merge:

# .github/workflows/tf-security.yml
- name: Run Checkov
  uses: bridgecrewio/checkov-action@master
  with:
    directory: ./terraform
    check: CKV_TF_1  # also add custom checks for sensitive outputs
    soft_fail: false

2. OPA / Conftest policy to enforce the flag on known-sensitive output names:

# policy/sensitive_outputs.rego
package terraform.outputs

deny[msg] {
  output := input.configuration.root_module.outputs[name]
  sensitive_pattern(name)
  not output.sensitive
  msg := sprintf("Output '%v' appears sensitive but is missing sensitive = true", [name])
}

sensitive_pattern(name) {
  patterns := ["password", "secret", "token", "key", "credential", "private"]
  p := patterns[_]
  contains(lower(name), p)
}
terraform show -json plan.tfplan | conftest test --policy policy/ -

3. tfsec inline scan:

tfsec . --minimum-severity HIGH

tfsec rule GEN006 flags outputs that reference sensitive resource attributes without the flag.

4. Pre-commit hook:

# .pre-commit-config.yaml
repos:
  - repo: https://github.com/antonbabenko/pre-commit-terraform
    rev: v1.83.5
    hooks:
      - id: terraform_tfsec
      - id: terraform_checkov

Ship none of these fixes manually. The pre-commit hook and CI gate together mean this class of misconfiguration never reaches a plan file in the first place.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.Does adding `sensitive = true` to an output actually encrypt the value in Terraform state?
No. `sensitive = true` is a superficial guardrail that only suppresses the value from CLI output and plan UI, leaving the raw secret exposed in plaintext within `terraform.tfstate`. This creates a critical security gap, as the state file itself becomes a single point of failure. To achieve true protection, you must architect a secure remote backend with server-side encryption (e.g., S3 + KMS) and enforce strict IAM policies—a complex, manual configuration prone to oversight.
Q2.Why does Terraform propagate sensitivity from a resource attribute to my output automatically?
Terraform's sensitivity taint is a brittle, transitive type system that automatically marks any expression referencing a sensitive attribute, even through indirect interpolation. While intended as a safeguard, it forces developers into a manual, error-prone acknowledgment process. Manually adding `sensitive = true` is a ticking time bomb—one missed propagation or misconfigured indirect reference can silently expose credentials in CI/CD logs. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix, eliminating manual risk.
Q3.Can I use `nonsensitive()` to bypass this error instead?
Technically yes, but this is a catastrophic security misconfiguration at scale. Using `nonsensitive()` to silence errors on real credentials will blast raw secrets across plan outputs and CI logs, creating immediate compliance violations. In large mono-repos or enterprise environments, manually auditing every use of `nonsensitive()` for legitimacy is a compliance nightmare and operationally unsustainable. Upgrade to the StackEngine CI/CD Automator (Team Edition) for on-prem, automated pull request generation with Maker-Checker review and policy enforcement across all Terraform codebases.