Secrets management in AWS: the right architecture at each scale
Most teams start with environment variables. By year two, it's a liability.
Stage 2: AWS Secrets Manager (prod-ready)
resource "aws_secretsmanager_secret" "db" {
name = "/${var.env}/${var.project}/database/password"
recovery_window_in_days = 7
}
resource "aws_secretsmanager_secret_rotation" "db" {
secret_id = aws_secretsmanager_secret.db.id
rotation_lambda_arn = aws_lambda_function.rotation.arn
rotation_rules { automatically_after_days = 30 }
}
Read at runtime (not deploy time):
import boto3, json
def get_secret(name: str) -> dict:
return json.loads(
boto3.client("secretsmanager").get_secret_value(SecretId=name)["SecretString"]
)
db = get_secret("/prod/payment-api/database/password")
IAM policy (least privilege):
resource "aws_iam_role_policy" "read_secrets" {
role = aws_iam_role.app.id
policy = jsonencode({ Statement = [{
Effect = "Allow"
Action = ["secretsmanager:GetSecretValue"]
Resource = ["arn:aws:secretsmanager:*:*:secret:/${var.env}/${var.project}/*"]
}]})
}
Parameter Store vs Secrets Manager
Parameter Store: non-sensitive config — FREE (standard tier)
Secrets Manager: actual secrets — ~$0.40/secret/month + auto-rotation
Most common mistakes
- Fetching secrets at container start (not runtime) — rotation doesn't take effect until restart
-
secretsmanager:*instead ofGetSecretValueonly — over-permissioned - No resource-based policy for cross-account access
- Not using KMS CMK
Step2Dev creates the Secrets Manager namespace and wires task IAM policies for every project.
What secrets management issue has caused you the most pain?
Top comments (0)