Secrets Manager and Parameter Store
Applications need configuration values (feature flags, endpoint URLs) and secrets (database passwords, API keys, certificates). AWS provides two services for this: Secrets Manager for secrets with automatic rotation, and Parameter Store for general configuration.
Secrets Manager vs Parameter Store
Section titled “Secrets Manager vs Parameter Store”| Feature | Secrets Manager | Parameter Store |
|---|---|---|
| Purpose | Secrets (passwords, API keys, tokens) | Configuration values and secrets |
| Automatic rotation | Yes (built-in, Lambda-based) | No |
| Cost | $0.40/secret/month + $0.05/10K API calls | Free for standard (up to 10K parameters); advanced tier: $0.05/parameter/month |
| Max size | 64 KB | 4 KB (standard) / 8 KB (advanced) |
| Encryption | Always encrypted (KMS) | Optional (SecureString uses KMS) |
| Cross-account | Yes (resource policies) | No |
| Versioning | Yes (automatic) | Yes (labels) |
Rule of thumb: Use Secrets Manager for credentials that need rotation (DB passwords, API keys). Use Parameter Store for everything else (config values, feature flags, non-sensitive settings).
Secrets Manager
Section titled “Secrets Manager”Storing a Secret
Section titled “Storing a Secret”# Create a secretaws secretsmanager create-secret \ --name prod/database/credentials \ --description "Production database credentials" \ --secret-string '{"username":"admin","password":"S3cure!Pass","host":"mydb.cluster-xyz.rds.amazonaws.com","port":"5432","dbname":"myapp"}'Retrieving a Secret
Section titled “Retrieving a Secret”# CLIaws secretsmanager get-secret-value --secret-id prod/database/credentials# Python (boto3)import boto3, json
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId='prod/database/credentials')secret = json.loads(response['SecretString'])
db_host = secret['host']db_pass = secret['password']Using in Lambda
Section titled “Using in Lambda”import boto3, json, os
secrets_client = boto3.client('secretsmanager')
def get_db_credentials(): response = secrets_client.get_secret_value( SecretId=os.environ['DB_SECRET_ARN'] ) return json.loads(response['SecretString'])
def handler(event, context): creds = get_db_credentials() # connect to database using creds['host'], creds['username'], creds['password']Tip: Cache the secret value in a module-level variable so you don’t call Secrets Manager on every Lambda invocation. The AWS SDK provides a caching layer for this.
Using in ECS Task Definitions
Section titled “Using in ECS Task Definitions”ECS can inject secrets directly as environment variables:
{ "containerDefinitions": [{ "name": "app", "secrets": [ { "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/database/credentials:password::" }, { "name": "API_KEY", "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api-key" } ] }]}The valueFrom ARN can reference a specific JSON key within the secret (:password:: above).
Using in CodeBuild
Section titled “Using in CodeBuild”env: secrets-manager: DB_PASSWORD: prod/database/credentials:password API_KEY: prod/api-keyAutomatic Rotation
Section titled “Automatic Rotation”Secrets Manager can rotate secrets automatically using a Lambda function:
┌──────────────┐ trigger ┌──────────────┐ update ┌──────────────┐│ Secrets │─────────────────►│ Rotation │──────────────────►│ Database ││ Manager │ │ Lambda │ │ (new pass) ││ (schedule) │◄─────────────────│ │ └──────────────┘│ │ store new secret│ │└──────────────┘ └──────────────┘# Enable rotation for an RDS secret (AWS provides built-in rotation Lambdas)aws secretsmanager rotate-secret \ --secret-id prod/database/credentials \ --rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSRotation \ --rotation-rules AutomaticallyAfterDays=30How rotation works (4 steps):
- createSecret — Generate a new password and store it as
AWSPENDING. - setSecret — Update the database with the new password.
- testSecret — Verify the new password works.
- finishSecret — Mark the new version as
AWSCURRENT.
If any step fails, the old password remains active — no downtime.
Naming Conventions
Section titled “Naming Conventions”Use a hierarchical naming pattern:
{environment}/{service}/{secret-type}
prod/database/credentialsprod/api/stripe-keystaging/database/credentialsdev/api/sendgrid-keyThis makes it easy to control access with IAM policies:
{ "Effect": "Allow", "Action": "secretsmanager:GetSecretValue", "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/*"}Systems Manager Parameter Store
Section titled “Systems Manager Parameter Store”Parameter Store is a simpler, cheaper option for configuration values and secrets that don’t need rotation.
Parameter Types
Section titled “Parameter Types”| Type | Encryption | Use Case |
|---|---|---|
| String | No | Feature flags, URLs, non-sensitive config |
| StringList | No | Comma-separated lists |
| SecureString | Yes (KMS) | Passwords, API keys (without rotation) |
Storing Parameters
Section titled “Storing Parameters”# Plain stringaws ssm put-parameter \ --name /myapp/prod/db_host \ --value "mydb.cluster-xyz.rds.amazonaws.com" \ --type String
# Secure string (encrypted)aws ssm put-parameter \ --name /myapp/prod/db_password \ --value "S3cure!Pass" \ --type SecureString
# With a specific KMS keyaws ssm put-parameter \ --name /myapp/prod/api_key \ --value "sk_live_abc123" \ --type SecureString \ --key-id alias/myapp-keyRetrieving Parameters
Section titled “Retrieving Parameters”# Single parameteraws ssm get-parameter --name /myapp/prod/db_host
# Decrypt SecureStringaws ssm get-parameter --name /myapp/prod/db_password --with-decryption
# Get all parameters under a pathaws ssm get-parameters-by-path --path /myapp/prod/ --with-decryption --recursiveimport boto3
ssm = boto3.client('ssm')
# Single parameterresponse = ssm.get_parameter(Name='/myapp/prod/db_host')db_host = response['Parameter']['Value']
# Multiple parameters by pathresponse = ssm.get_parameters_by_path( Path='/myapp/prod/', WithDecryption=True, Recursive=True)config = {p['Name'].split('/')[-1]: p['Value'] for p in response['Parameters']}# {'db_host': '...', 'db_password': '...', 'api_key': '...'}Using in ECS
Section titled “Using in ECS”{ "containerDefinitions": [{ "name": "app", "secrets": [ { "name": "DB_HOST", "valueFrom": "arn:aws:ssm:us-east-1:123456789012:parameter/myapp/prod/db_host" } ] }]}Using in Lambda (Environment Variables)
Section titled “Using in Lambda (Environment Variables)”Reference Parameter Store values in CloudFormation/SAM/CDK:
# SAM templateEnvironment: Variables: DB_HOST: !Sub "{{resolve:ssm:/myapp/prod/db_host}}" DB_PASSWORD: !Sub "{{resolve:ssm-secure:/myapp/prod/db_password}}"Using in Terraform
Section titled “Using in Terraform”data "aws_ssm_parameter" "db_host" { name = "/myapp/prod/db_host"}
data "aws_ssm_parameter" "db_password" { name = "/myapp/prod/db_password" with_decryption = true}
resource "aws_ecs_task_definition" "app" { container_definitions = jsonencode([{ environment = [ { name = "DB_HOST", value = data.aws_ssm_parameter.db_host.value } ] secrets = [ { name = "DB_PASSWORD", valueFrom = data.aws_ssm_parameter.db_password.arn } ] }])}Parameter Store Tiers
Section titled “Parameter Store Tiers”| Feature | Standard | Advanced |
|---|---|---|
| Max parameters | 10,000 | 100,000 |
| Max size | 4 KB | 8 KB |
| Parameter policies | No | Yes (expiration, notification) |
| Cost | Free | $0.05/parameter/month |
Best Practices
Section titled “Best Practices”| Practice | Why |
|---|---|
| Never hardcode secrets in code | Git history, container images, and logs expose them |
Use hierarchical naming (/env/service/key) | Easy IAM scoping and bulk retrieval |
| Encrypt with KMS (SecureString) | At-rest encryption for all secrets |
| Enable rotation for database credentials | Reduces risk of credential compromise |
| Use IAM roles for access (not access keys) | Temporary credentials, no key management |
| Cache secrets in application memory | Reduce API calls and latency |
| Audit access with CloudTrail | Know who accessed which secrets and when |
| Use separate secrets per environment | prod/* and dev/* with different IAM policies |
Key Takeaways
Section titled “Key Takeaways”- Secrets Manager is for secrets that need automatic rotation — DB passwords, API keys, certificates. Costs $0.40/secret/month.
- Parameter Store is for general configuration values and simpler secrets. Standard tier is free (up to 10K parameters).
- Both integrate with Lambda, ECS, CodeBuild, and Terraform for seamless secret injection.
- Use hierarchical naming (
/env/service/key) and scope IAM policies by path prefix. - Never hardcode secrets — use one of these services and inject at runtime.