How do you manage sensitive values in Terraform (variables, tfvars, Vault)?
Quick Answer
Sensitive values should never be hardcoded in Terraform files. Use the sensitive flag on variables to suppress plan output, store secrets in tools like HashiCorp Vault or AWS Secrets Manager, reference them via data sources, and ensure state encryption since Terraform state stores all values in plaintext.
Detailed Answer
Managing sensitive values in Terraform is one of the most critical aspects of production infrastructure and one of the most commonly mishandled. Think of it like handling classified documents in an office — you need secure storage (Vault), controlled access (IAM policies), no leaving copies around (state encryption), and no reading aloud in public (plan output suppression).
The first layer of defense is the sensitive variable flag introduced in Terraform 0.14. When you mark a variable as sensitive = true, Terraform redacts its value from plan and apply output, replacing it with (sensitive value). This prevents accidental exposure in CI/CD logs, terminal output shared in screenshots, or plan files sent for review. However, this is purely a display-level protection — the actual value is still stored in plaintext in the state file. Many engineers mistakenly believe the sensitive flag encrypts the value, but it does not.
The second and more important layer is keeping secrets out of your Terraform code and version control entirely. Never put passwords, API keys, or tokens in .tf files, .tfvars files, or any file committed to Git. Even if you add them to .gitignore, they can end up in Git history, CI/CD caches, or developer workstations. Instead, use external secret management systems.
HashiCorp Vault is the gold standard for dynamic secrets with Terraform. The Vault provider allows Terraform to read secrets from Vault at plan/apply time using data sources. Better yet, Vault can generate dynamic, short-lived database credentials that automatically expire — meaning even if the state file is compromised, the stolen credentials will be invalid within hours. The Vault provider authenticates using methods like AppRole, AWS IAM, or Kubernetes service accounts, avoiding the chicken-and-egg problem of needing a secret to access secrets.
AWS Secrets Manager and SSM Parameter Store are alternatives that integrate natively with AWS. You store secrets there via the AWS console or CLI, and Terraform reads them with data sources. This approach keeps the secret out of your HCL code and .tfvars files but still stores the value in Terraform state after it is read.
The state file itself is the elephant in the room. Regardless of how carefully you manage inputs, Terraform state contains every attribute of every resource, including sensitive outputs like database connection strings, private keys, and IAM access keys. This is why remote state with encryption at rest (S3 server-side encryption, Terraform Cloud encryption) and strict access controls is non-negotiable. In production, teams typically restrict state file access to CI/CD service accounts and a small group of senior engineers.
Environment variables (TF_VAR_name) provide another mechanism for passing secrets without files. CI/CD systems like GitHub Actions, GitLab CI, and Jenkins can inject secrets as environment variables that Terraform reads automatically. This avoids writing secrets to disk entirely, though they may appear in process listings or CI/CD audit logs.
For the most sensitive operations, teams combine multiple strategies: Vault generates dynamic credentials, Terraform reads them via data sources, the sensitive flag suppresses output, state is encrypted and access-controlled, and CI/CD pipelines run in ephemeral containers that are destroyed after each run.