Compliance & SecOps

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.

·2 min read·
#SecretsManager#Security#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_days line.
  • "Who has access to production secrets?" → "Whoever the resource policy on prod/app/db grants secretsmanager:GetSecretValue to — five IAM roles, listed here."
  • "When was this secret last accessed?" → CloudTrail GetSecretValue events, queried by eventSource = secretsmanager.amazonaws.com.

The migration play

  1. For each .env entry: create the Secrets Manager secret in Terraform, populate the initial value out-of-band, attach the rotation Lambda.
  2. Update the app to fetch from Secrets Manager (cache for 5 minutes; that's safe).
  3. Delete the CI variable. Delete the .env. Add a git secrets pre-commit hook so nobody re-adds one.
  4. Run terraform plan quarterly 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.

More in Compliance & SecOps