Secrets Manager Fundamentals
Secrets Manager Fundamentals
AWS Secrets Manager helps you protect secrets needed to access applications, services, and IT resources.
Features
- Automatic secret rotation
- Centralized secret management
- Audit with CloudTrail
- Encryption with KMS
- Cross-account access
Create a Secret
# Create a secret for database credentials
aws secretsmanager create-secret \
--name myapp/prod/database \
--description "Production database credentials" \
--secret-string '{
"username": "admin",
"password": "SecurePass123!",
"host": "mydb.xxxx.us-east-1.rds.amazonaws.com",
"port": 3306,
"dbname": "myapp"
}'
# Create a secret from JSON file
aws secretsmanager create-secret \
--name myapp/prod/api-keys \
--secret-string file://secrets.json
# Create a secret with binary data
aws secretsmanager create-secret \
--name myapp/prod/certificate \
--secret-binary fileb://cert.pfx
# List secrets
aws secretsmanager list-secrets
# Get secret value
aws secretsmanager get-secret-value --secret-id myapp/prod/database
Secret Structure
{
"username": "admin",
"password": "SecurePass123!",
"host": "mydb.xxxx.us-east-1.rds.amazonaws.com",
"port": 3306,
"dbname": "myapp",
"engine": "mysql",
"engineVersion": "8.0"
}
Secret Rotation
Secret Rotation
Automatic Rotation for RDS
# Create rotation function
aws lambda create-function \
--function-name secret-rotation \
--runtime python3.12 \
--role arn:aws:iam::xxx:role/rotation-role \
--handler lambda_function.lambda_handler \
--zip-file fileb://rotation.zip
# Enable rotation
aws secretsmanager rotate-secret \
--secret-id myapp/prod/database \
--rotation-lambda-arn arn:aws:lambda:us-east-1:xxx:function:secret-rotation \
--rotation-rules '{
"AutomaticallyAfterDays": 30
}'
# Check rotation status
aws secretsmanager describe-secret --secret-id myapp/prod/database
Rotation Lambda Function
import boto3
import json
import pymysql
def lambda_handler(event, context):
secret_arn = event['SecretId']
token = event['ClientRequestToken']
step = event['Step']
service_client = boto3.client('secretsmanager')
if step == 'createSecret':
# Generate new secret
current = json.loads(service_client.get_secret_value(SecretId=secret_arn)['SecretString'])
new_password = generate_password()
new_secret = {**current, 'password': new_password}
service_client.put_secret_value(
SecretId=secret_arn,
SecretString=json.dumps(new_secret),
VersionStages=['AWSPENDING'],
VersionId=token
)
elif step == 'setSecret':
# Set the new password on the database
pending = json.loads(service_client.get_secret_value(
SecretId=secret_arn, VersionStage='AWSPENDING')['SecretString'])
conn = pymysql.connect(host=pending['host'], user=pending['username'], password=pending['password'])
cursor = conn.cursor()
cursor.execute(f"ALTER USER '{pending['username']}'@'%' IDENTIFIED BY '{new_password}'")
conn.commit()
elif step == 'testSecret':
# Test the new secret
pending = json.loads(service_client.get_secret_value(
SecretId=secret_arn, VersionStage='AWSPENDING')['SecretString'])
conn = pymysql.connect(host=pending['host'], user=pending['username'], password=pending['password'])
conn.close()
elif step == 'finishSecret':
# Mark new secret as current
service_client.update_secret_version_stage(
SecretId=secret_arn,
VersionStage='AWSCURRENT',
MoveToVersionId=token
)
Using Secrets in AWS Services
Using Secrets in AWS Services
Lambda
import boto3
import json
def lambda_handler(event, context):
# Get secret
secrets = boto3.client('secretsmanager')
secret = json.loads(secrets.get_secret_value(SecretId='myapp/prod/database')['SecretString'])
# Use secret
connection = pymysql.connect(
host=secret['host'],
user=secret['username'],
password=secret['password'],
database=secret['dbname']
)
ECS Task Definition
{
"containerDefinitions": [{
"name": "my-app",
"image": "my-app:latest",
"secrets": [{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:xxx:secret:myapp/prod/database:password::"
}],
"environment": [{
"name": "DB_HOST",
"value": "mydb.xxxx.us-east-1.rds.amazonaws.com"
}]
}]
}
EC2 with SSM
# Retrieve secret on EC2
aws secretsmanager get-secret-value --secret-id myapp/prod/database --query 'SecretString' --output text | jq -r '.password'
# Or use SSM Parameter Store with hierarchy
aws ssm get-parameters-by-path \
--path /myapp/prod/ \
--recursive \
--with-decryption
CloudFormation
Resources:
MySecret:
Type: AWS::SecretsManager::Secret
Properties:
Name: myapp/prod/database
Description: Database credentials
SecretString: !Sub '{"username":"admin","password":"${RandomPassword}"}'
KmsKeyId: alias/aws/secretsmanager
MyRotation:
Type: AWS::SecretsManager::RotationSchedule
Properties:
SecretId: !Ref MySecret
RotationLambdaARN: !GetAtt RotationFunction.Arn
RotationRules:
AutomaticallyAfterDays: 30
Secret Policies and Access Control
Secret Policies and Access Control
Resource Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
},
{
"Sid": "DenyExternalAccess",
"Effect": "Deny",
"Principal": "*",
"Action": "secretsmanager:*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": "us-east-1"
}
}
}
]
}
IAM Policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:myapp/prod/*"
},
{
"Effect": "Allow",
"Action": "secretsmanager:ListSecrets",
"Resource": "*"
}
]
}
Cross-Account Access
# Share secret with another account
aws secretsmanager put-resource-policy \
--secret-id myapp/prod/database \
--resource-policy '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::OTHER_ACCOUNT:root"
},
"Action": "secretsmanager:GetSecretValue",
"Resource": "*"
}]
}'
Secrets Manager Best Practices
Secrets Manager Best Practices
Cost Optimization
| Feature | Cost |
|---|---|
| Secret storage | $0.40/secret/month |
| API calls | $0.05/10,000 calls |
| Rotation | $0.05/10,000 calls |
Best Practices
- Use naming conventions:
env/service/secret-name - Enable rotation for all database credentials
- Use resource policies to limit access
- Monitor access with CloudTrail
- Use encryption context for additional security
- Tag secrets for cost allocation and organization
- Use replication for disaster recovery
- Never hardcode secrets in code or environment variables
Secrets Manager vs SSM Parameter Store
| Feature | Secrets Manager | SSM Parameter Store |
|---|---|---|
| Cost | $0.40/secret/month | Free tier available |
| Rotation | Built-in | Manual |
| Versioning | Yes | Yes |
| Encryption | KMS | KMS or Standard |
| Cross-region | Replication | No |
Monitoring
# Monitor secret access
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=GetSecretValue \
--max-results 10
# Check for failed rotation
aws cloudwatch get-metric-statistics \
--namespace AWS/SecretsManager \
--metric-name RotationsFailed \
--dimensions Name=SecretName,Value=myapp/prod/database \
--start-time $(date -u -d '24 hours ago') \
--end-time $(date -u) \
--period 86400 \
--statistics Sum