How do you manage secrets in Terraform configurations safely?

5 minintermediateterraformsecretssecurity

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 = true only 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 output or read the raw state file, since sensitive values remain retrievable there even when hidden from normal plan/apply output.

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.

Related Resources