Terraform Sensitive Variables and Secrets Management
Infrastructure code manages credentials, connection strings, API keys, and passwords every day. Handling these secrets poorly — pasting them into code, committing them to Git, or printing them in logs — creates serious security risks. This topic covers Terraform's built-in sensitive value handling and the strategies professionals use to keep secrets safe.
The Core Problem
Terraform needs certain secret values to create and configure infrastructure — an RDS master password, an API key for a third-party service, an SSH private key. These values must reach Terraform somehow. The question is: how do you get them there without exposing them?
Marking Variables as Sensitive
Add sensitive = true to any variable declaration to tell Terraform to redact the value from all output.
variable "db_password" {
description = "Master password for the RDS database"
type = string
sensitive = true
}
resource "aws_db_instance" "main" {
identifier = "main-db"
engine = "postgres"
instance_class = "db.t3.medium"
password = var.db_password
}
With sensitive = true, Terraform replaces the value with (sensitive value) everywhere it would normally appear:
# In terraform plan output: + password = (sensitive value) # In terraform apply output: Changes to Outputs: + db_connection = (sensitive value)
The value is still stored in the state file. Sensitive marking only hides values from terminal output — it does not encrypt them in state.
Marking Outputs as Sensitive
When a resource attribute contains a secret and you expose it as an output, mark that output sensitive too.
output "db_connection_string" {
description = "Full database connection string"
value = "postgres://${var.db_user}:${var.db_password}@${aws_db_instance.main.endpoint}/appdb"
sensitive = true
}
Terraform automatically propagates sensitivity — if you use a sensitive variable inside any expression, the result of that expression also becomes sensitive. This propagation means you often do not need to manually mark outputs as sensitive; Terraform does it for you.
How to Provide Sensitive Values Safely
Never put secret values in terraform.tfvars files committed to Git. Use these safe methods instead:
Method 1: Environment Variables
export TF_VAR_db_password="super-secret-password-123" terraform apply
Environment variables work well in CI/CD pipelines. Most platforms (GitHub Actions, GitLab CI, CircleCI) provide encrypted secret storage that injects values as environment variables at runtime.
Method 2: Interactive Prompt
Declare a variable with no default and mark it sensitive. Terraform prompts for the value at runtime:
variable "db_password" {
type = string
sensitive = true
# No default — Terraform will prompt
}
$ terraform apply var.db_password Enter a value: (input hidden)
The terminal hides the input as you type.
Method 3: AWS Secrets Manager or HashiCorp Vault
Fetch secrets from a dedicated secret store at apply time using a data source. The secret never appears in code or variable files.
# Fetch a secret from AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/myapp/db_password"
}
resource "aws_db_instance" "main" {
identifier = "main-db"
engine = "postgres"
instance_class = "db.t3.medium"
password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}
Diagram: Secret Manager Flow
AWS Secrets Manager
"prod/myapp/db_password": { "password": "s3cr3t" }
|
| data source reads at plan time
v
Terraform configuration uses the secret value
|
v
RDS instance created with correct password
|
Secret value never appears in:
- .tf files
- .tfvars files
- Git history
- Terminal output (sensitive = true propagates)
Protecting the State File
Sensitive values are stored in the Terraform state file — even values marked as sensitive. Anyone who can read the state file can read the secrets. Protect state files with:
- S3 server-side encryption: Enable AES-256 or KMS encryption on the state bucket
- Strict IAM policies: Only specific roles and users can read the state bucket — not the whole organisation
- Versioning: Enable S3 versioning to recover from accidental corruption or overwrites
- Access logging: Enable S3 access logs to track who reads or writes the state bucket
What Never to Do
- Never commit
terraform.tfvarsfiles containing passwords to version control - Never hard-code secrets directly in
.tffiles - Never pass secrets as command-line flags (
-var="password=secret") — shell history stores them - Never print secrets in output blocks unless they are marked sensitive
Key Points
- Mark variables and outputs as
sensitive = trueto redact them from terminal and plan output. - Terraform propagates sensitivity automatically — expressions using sensitive inputs also become sensitive.
- Provide secrets through environment variables, interactive prompts, or secret manager data sources — never in committed files.
- State files store sensitive values in plaintext — encrypt the state backend and restrict access strictly.
- AWS Secrets Manager, Azure Key Vault, and HashiCorp Vault are the recommended stores for secrets used by Terraform.
