How do you manage secrets in Terraform configurations safely?
Quick Answer
Never hardcode secrets in `.tf`/`.tfvars` files or commit them to version control. Instead, pull secrets at apply time from a dedicated secrets manager (Vault, AWS Secrets Manager, Azure Key Vault) via a data source, or inject them as environment variables (`TF_VAR_db_password`) from your CI system's secret store. Mark the corresponding variable `sensitive = true` so Terraform redacts it from CLI/plan output (note this does **not** encrypt it in the state file, which still needs a secured, encrypted backend). Keep tfvars files containing real secrets out of git entirely, using `.gitignore` and per-environment secret injection instead.
Detailed Answer
Secrets management is one of the areas where naive Terraform usage goes wrong most often — the tempting shortcuts (hardcoding a password, committing a .tfvars file) are exactly the ones that cause real incidents.
What not to do
# Never do this:
resource "aws_db_instance" "main" {
password = "SuperSecret123!" # plaintext in .tf, committed to git
}
# terraform.tfvars — also never commit this if it contains real secrets
db_password = "SuperSecret123!"
Both put the secret in version control history permanently (even if later removed, it remains in git log), visible to anyone with repo access, indefinitely.
Better patterns
1. Pull from a secrets manager at apply time, via a data source:
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/password"
}
resource "aws_db_instance" "main" {
password = data.aws_secretsmanager_secret_version.db_password.secret_string
}
2. Inject via environment variables in CI, sourced from a secret store:
export TF_VAR_db_password="$(vault kv get -field=password secret/db)"
terraform apply
variable "db_password" {
type = string
sensitive = true
}
3. Generate secrets Terraform itself creates, and immediately hand them to a secrets manager rather than exposing them as plain outputs:
resource "random_password" "db" {
length = 24
special = true
}
resource "aws_secretsmanager_secret_version" "db" {
secret_id = aws_secretsmanager_secret.db.id
secret_string = random_password.db.result
}
Remaining caveats
sensitive = trueonly redacts CLI/log output — it does not encrypt the value inside the state file, so the backend itself (S3 + KMS, Terraform Cloud) still needs to be properly access-controlled and encrypted at rest.- Restrict who can run
terraform outputor read the raw state file, since sensitive values remain retrievable there even when hidden from normalplan/applyoutput.
Interview-ready summary
Never hardcode secrets in configuration or commit them in tfvars; pull them from a secrets manager at apply time or inject via CI-managed environment variables, and treat the backend/state storage itself as the real security boundary that needs encryption and access control.