Crisp answer: Never store secrets in .tf files or state directly. Pass
them via environment variables or reference them from a secrets manager at
runtime. Mark outputs as sensitive to prevent them appearing in logs.
The problem with secrets in Terraform:
Terraform state stores every resource attribute in plaintext JSON. If a resource accepts a password (like an RDS master password), that password ends up in the state file. Encrypting the state backend helps but the secret is still there.
Pattern 1 — Let the provider generate the secret:
For RDS, Aurora, and ElastiCache, use Secrets Manager-managed credentials:
resource "aws_rds_cluster" "main" {
manage_master_user_password = true # AWS generates and rotates the password
# No password in your config or state
}
The password is in Secrets Manager, rotated automatically, never in Terraform state.
Pattern 2 — Reference existing secrets, never create them:
# Reference an existing Secrets Manager secret — do not create it in Terraform
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/myapp/db_password" # created manually or via separate process
}
resource "aws_db_instance" "main" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}
The secret value still ends up in state. Better than hardcoding in config, but not ideal.
Pattern 3 — Environment variables:
export TF_VAR_db_password="secret123"
terraform apply
variable "db_password" {
type = string
sensitive = true # prevents value appearing in plan output or logs
}
The value does not appear in .tf files or version control. It still ends
up in state if used directly.
Pattern 4 — Vault provider (most secure):
provider "vault" {
address = "https://vault.internal:8200"
}
data "vault_generic_secret" "db" {
path = "secret/prod/database"
}
resource "aws_db_instance" "main" {
password = data.vault_generic_secret.db.data["password"]
}
Marking outputs as sensitive:
output "db_password" {
value = aws_db_instance.main.password
sensitive = true # value is redacted in terminal output
# Still in state — just not printed in logs
}
Best practice summary:
| Approach | Config | State | Logs |
|---|---|---|---|
| Hardcoded in .tf | ✗ plaintext | ✗ plaintext | ✗ visible |
| Variable + sensitive | ✓ no value | ✗ plaintext | ✓ redacted |
| Data source reference | ✓ only path | ✗ plaintext | ✓ redacted |
| AWS-managed (Secrets Manager) | ✓ | ✓ no value | ✓ |
| Vault provider | ✓ only path | ✗ depends | ✓ |
What to say in the interview:
"The Terraform state file is the main concern — it stores every resource attribute and sensitive values end up there. My preferred approaches: for AWS RDS and Aurora, use the manage_master_user_password flag so AWS generates and rotates the password in Secrets Manager and it never touches state. For other secrets, mark variables as sensitive so they're redacted in logs, and use environment variables or Vault to inject values at apply time rather than storing them in config. Never hardcode secrets in .tf files because those go into version control."