The Death of .env Files: Automated Secret Rotation with Terraform
Hardcoded secrets in CI/CD variables are a compliance failure waiting to happen. A walkthrough of AWS Secrets Manager rotation, codified in Terraform.
A .env file in a repo is the most common SOC2 finding I see. A .env file in a CI variable is the second. Both share the same root cause: a secret with no expiry, no audit trail, and no owner.
Secrets Manager + Terraform fixes all three in about 40 lines of HCL.
The Terraform module
resource "aws_secretsmanager_secret" "db" {
name = "prod/app/db"
kms_key_id = aws_kms_key.app.arn
recovery_window_in_days = 7
}
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotate_db.arn
rotation_rules {
automatically_after_days = 30
}
}
The Lambda is one of AWS's published rotation templates — for RDS, you don't even write it. Drop in the SAR app, point it at your secret, done.
What the app gets
The app NEVER holds the password. It holds an IAM role. On startup (or per request, if you're paranoid) it calls secretsmanager:GetSecretValue. The SDK caches the value for the connection pool's lifetime. On rotation, the new version is published with AWSCURRENT, the old one becomes AWSPREVIOUS for a grace window, and connections drain to the new secret without downtime.
The audit answers
- "How often are credentials rotated?" → "Every 30 days, enforced by Terraform." Show the
automatically_after_daysline. - "Who has access to production secrets?" → "Whoever the resource policy on
prod/app/dbgrantssecretsmanager:GetSecretValueto — five IAM roles, listed here." - "When was this secret last accessed?" → CloudTrail
GetSecretValueevents, queried byeventSource = secretsmanager.amazonaws.com.
The migration play
- For each
.enventry: create the Secrets Manager secret in Terraform, populate the initial value out-of-band, attach the rotation Lambda. - Update the app to fetch from Secrets Manager (cache for 5 minutes; that's safe).
- Delete the CI variable. Delete the
.env. Add agit secretspre-commit hook so nobody re-adds one. - Run
terraform planquarterly to prove the rotation cadence hasn't drifted.
The goal isn't "we have a secrets manager." Plenty of compromised companies had one. The goal is "no human ever sees a production credential, and the audit trail proves it."
Further reading: Secrets Manager rotation.