How do you handle sensitive data in Terraform state?

5 minadvancedterraformstatesecuritysecrets

Quick Answer

Terraform state stores resource attributes **in plaintext**, including anything marked `sensitive = true` in configuration (sensitivity only affects CLI/log output, not the state file itself) — so a database password set via a resource argument ends up readable in state. Mitigate this by: using a remote backend with encryption at rest (S3 + SSE/KMS) and strict access controls (IAM, not world-readable buckets), avoiding state as a place to store long-lived secrets at all (pull them from Vault/Secrets Manager at apply time instead), and enabling audit logging on who can read the state.

Detailed Answer

A subtle but important gotcha: marking a variable sensitive = true only changes what Terraform prints — it does not encrypt or omit that value from the state file itself.

variable "db_password" {
  type      = string
  sensitive = true
}

resource "aws_db_instance" "main" {
  password = var.db_password   # ends up in plaintext in terraform.tfstate
}

Run terraform apply and the CLI output will show password = (sensitive value) — but open terraform.tfstate directly and the real password is sitting there in plaintext, because Terraform must retain the actual value to know if it changed on the next plan.

How to actually protect this data

  1. Secure the backend, not just the value. Store state in a backend with encryption at rest (S3 with SSE-KMS, Terraform Cloud's encrypted storage) and strict IAM/RBAC so only the CI role and authorized operators can read it. Enable access logging/audit trails on the state bucket.
  2. Avoid putting long-lived secrets in Terraform at all where possible. Instead of a Terraform variable holding a real password, use a resource that generates a random password and immediately stores it in a secrets manager (random_passwordaws_secretsmanager_secret_version), so the application reads the secret from Secrets Manager/Vault at runtime rather than from a Terraform output.
  3. Never commit .tfvars files containing real secrets. Inject them via CI secret stores (TF_VAR_db_password env var) instead.
  4. Restrict who can run terraform output or read state directly — sensitive outputs are also stored in plaintext in state even though the CLI hides them by default.
  5. Consider state encryption features (available in newer Terraform versions / Terraform Cloud) that encrypt the state file itself at rest, on top of backend-level encryption.

Interview-ready summary

sensitive = true is a UI/UX safeguard against accidental screen/log exposure, not an encryption mechanism. Real protection comes from securing the backend storage and minimizing how many genuine secrets ever flow through Terraform state in the first place.

Related Resources