Home DevOps AWS Secrets Manager: Secure Credential Rotation
Intermediate 7 min · July 12, 2026

AWS Secrets Manager: Secure Credential Rotation

A comprehensive guide to AWS Secrets Manager: Secure Credential Rotation on AWS, covering core concepts, configuration, best practices, and real-world use cases..

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 12, 2026
last updated
2,073
articles · all by Naren
Before you start⏱ 25 min
  • Basic understanding of AWS services and cloud computing concepts.
✦ Definition~90s read
What is AWS Secrets Manager?

AWS Secrets Manager is a fully managed service for securely storing, rotating, and retrieving database credentials, API keys, and other secrets throughout their lifecycle. It eliminates hardcoded secrets in code by providing automatic rotation at a schedule you define, with built-in integration for RDS, Redshift, and DocumentDB.

AWS Secrets Manager: Secure Credential Rotation is like having a smart assistant that handles the heavy lifting so you don't have to manage servers yourself.

Use it when you need to enforce credential rotation policies, audit secret access, or replicate secrets across regions for disaster recovery.

Plain-English First

AWS Secrets Manager: Secure Credential Rotation is like having a smart assistant that handles the heavy lifting so you don't have to manage servers yourself.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

You just pushed a code change that rotated database credentials. Now your production app is down because the old password was cached in a connection pool. This is the reality of manual rotation — it's error-prone and risky. AWS Secrets Manager solves this by automating the entire lifecycle: creation, rotation, and deletion of secrets. But here's the kicker: most teams use it wrong. They enable automatic rotation without understanding the underlying Lambda execution model, leading to race conditions and expired credentials during rotation windows. In this post, I'll show you how to configure rotation correctly, handle edge cases like multi-AZ failovers, and avoid the pitfalls that take down production.

Why Manual Credential Rotation Fails in Production

Manual credential rotation is a ticking time bomb. In production, teams often rely on shared secrets stored in config files, environment variables, or even hardcoded in source code. When a credential expires or leaks, the scramble to rotate it manually leads to downtime, inconsistent state across services, and security gaps. I've seen incidents where a database password expired at 3 AM, taking down a critical microservice because no one remembered to update the secret in the CI/CD pipeline. The root cause: no automated rotation. AWS Secrets Manager solves this by centralizing secret storage and enabling automatic rotation via Lambda functions. But the real challenge is designing rotation that doesn't break your applications. You need to ensure that during rotation, both old and new credentials are valid simultaneously (grace period), and that clients refresh secrets without restarting. This section sets the stage for why you need a robust rotation strategy, not just a tool.

manual-rotation-fail.shBASH
1
2
3
4
5
6
7
8
# Simulating a manual rotation failure
# Old secret in env
DB_PASSWORD="old_password_123"
# New secret set manually
DB_PASSWORD="new_password_456"
# Application still uses old connection pool
# => Connection errors until restart
# No audit trail, no rollback plan
Output
ERROR 1045 (28000): Access denied for user 'app'@'10.0.0.1' (using password: YES)
⚠ Shared Secrets Are a Single Point of Failure
If you're storing secrets in environment variables or config files, you're one accidental commit away from a breach. Automate rotation to reduce blast radius.
📊 Production Insight
In a past incident, a team rotated a database password but forgot to update the read replica's secret, causing data inconsistency for hours.
🎯 Key Takeaway
Manual rotation is error-prone and causes downtime; automate with AWS Secrets Manager.
aws-secrets-manager THECODEFORGE.IO Zero-Downtime Credential Rotation Flow Step-by-step process for rotating secrets without service interruption Initiate Rotation Trigger via schedule or manual request Create New Secret Version Generate new credential in Secrets Manager Update Application Config Deploy new secret to app instances gradually Verify New Credential Test connectivity and functionality Deprecate Old Secret Mark previous version as pending deletion Complete Rotation Remove old secret after grace period ⚠ Skipping verification can cause silent failures Always test new credentials before deprecating old ones THECODEFORGE.IO
thecodeforge.io
Aws Secrets Manager

AWS Secrets Manager: Architecture and Key Concepts

AWS Secrets Manager is a service for securely storing, retrieving, and rotating secrets. At its core, it stores secrets as encrypted blobs in a dedicated Secrets Manager instance, with encryption handled by AWS KMS. Each secret has a name, description, and rotation configuration. The service integrates with AWS Lambda for custom rotation logic. Key concepts: secret versions (staged labels like AWSCURRENT, AWSPREVIOUS, AWSPENDING), automatic rotation schedule (e.g., every 30 days), and resource-based policies for cross-account access. The rotation process works by creating a new version (AWSPENDING), updating the credential in the target service, testing it, and then marking it as AWSCURRENT while deprecating the old one. This ensures zero-downtime rotation if implemented correctly. However, the default rotation templates (e.g., for RDS) may not fit all use cases—you often need custom Lambda functions for non-standard services or complex validation. Understanding these concepts is critical before writing rotation code.

create-secret.shBASH
1
2
3
4
5
6
aws secretsmanager create-secret \
    --name prod/myapp/db-password \
    --description "Database password for MyApp production" \
    --secret-string '{"username":"myapp","password":"InitialPassword123!"}' \
    --kms-key-id alias/aws/secretsmanager \
    --tags Key=Environment,Value=prod
Output
{
"ARN": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-password-abc123",
"Name": "prod/myapp/db-password",
"VersionId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
🔥Secret Versioning Is Your Safety Net
Always use AWSPREVIOUS for rollback. If a rotation fails, you can revert to the previous version without manual intervention.
📊 Production Insight
We once had a rotation Lambda that accidentally deleted the AWSPREVIOUS version, leaving no rollback option. Always preserve at least two versions.
🎯 Key Takeaway
Secrets Manager uses versioning and staging labels to manage rotation safely.

Designing a Custom Rotation Lambda for Zero Downtime

The default rotation templates work for simple cases, but production often requires custom logic. For example, rotating a database password for a multi-region deployment or a service that doesn't support concurrent credentials. The Lambda function must implement four steps: createSecret, setSecret, testSecret, and finishSecret. In createSecret, you generate a new password and store it as AWSPENDING. In setSecret, you update the target service (e.g., RDS) with the new password. In testSecret, you verify the new credential works (e.g., by making a test query). In finishSecret, you move AWSPENDING to AWSCURRENT and deprecate the old one. Critical design decisions: ensure the target service supports two active passwords during rotation (e.g., RDS allows multiple users with different passwords). If not, you need a different strategy, like updating all clients before finalizing. Also, handle failures gracefully—if setSecret fails, don't proceed to finishSecret. Use idempotent operations to avoid partial updates. The Lambda should have minimal permissions: only access to the specific secret and the target service.

rotation_lambda.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import boto3
import json
import logging
import os

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    arn = event['SecretId']
    token = event['ClientRequestToken']
    step = event['Step']
    
    secrets_client = boto3.client('secretsmanager')
    
    try:
        metadata = secrets_client.describe_secret(SecretId=arn)
        if not metadata['RotationEnabled']:
            logger.error("Secret %s is not enabled for rotation", arn)
            raise ValueError("Rotation not enabled")
        
        versions = metadata['VersionIdsToStages']
        if token not in versions:
            logger.error("Token %s not found in versions", token)
            raise ValueError("Invalid token")
        if 'AWSPENDING' in versions[token]:
            logger.info("Token %s is already AWSPENDING, skipping", token)
            return
        
        if step == 'createSecret':
            create_secret(secrets_client, arn, token)
        elif step == 'setSecret':
            set_secret(secrets_client, arn, token)
        elif step == 'testSecret':
            test_secret(secrets_client, arn, token)
        elif step == 'finishSecret':
            finish_secret(secrets_client, arn, token)
        else:
            raise ValueError("Invalid step %s" % step)
    except Exception as e:
        logger.error("Rotation failed: %s", str(e))
        raise

def create_secret(client, arn, token):
    # Generate new password
    new_password = generate_random_password()
    # Store as AWSPENDING
    client.put_secret_value(
        SecretId=arn,
        ClientRequestToken=token,
        SecretString=json.dumps({'username': 'myapp', 'password': new_password}),
        VersionStages=['AWSPENDING']
    )
    logger.info("Created AWSPENDING version for %s", arn)

def set_secret(client, arn, token):
    # Retrieve pending secret
    pending = client.get_secret_value(
        SecretId=arn,
        VersionId=token,
        VersionStage='AWSPENDING'
    )
    creds = json.loads(pending['SecretString'])
    # Update RDS password
    rds_client = boto3.client('rds')
    rds_client.modify_db_instance(
        DBInstanceIdentifier='mydb',
        MasterUserPassword=creds['password']
    )
    logger.info("Set new password on RDS instance")

def test_secret(client, arn, token):
    # Retrieve pending secret
    pending = client.get_secret_value(
        SecretId=arn,
        VersionId=token,
        VersionStage='AWSPENDING'
    )
    creds = json.loads(pending['SecretString'])
    # Test connection
    import pymysql
    try:
        conn = pymysql.connect(
            host='mydb.xxxxx.us-east-1.rds.amazonaws.com',
            user=creds['username'],
            password=creds['password'],
            database='mydb',
            connect_timeout=5
        )
        conn.close()
        logger.info("Test connection successful")
    except Exception as e:
        logger.error("Test connection failed: %s", str(e))
        raise

def finish_secret(client, arn, token):
    # Move AWSPENDING to AWSCURRENT
    client.update_secret_version_stage(
        SecretId=arn,
        VersionStage='AWSCURRENT',
        MoveToVersionId=token,
        RemoveFromVersionId='AWSPREVIOUS'
    )
    logger.info("Finished rotation for %s", arn)

def generate_random_password():
    import secrets
    import string
    alphabet = string.ascii_letters + string.digits + '!@#$%^&*()'
    return ''.join(secrets.choice(alphabet) for _ in range(32))
Output
INFO: Created AWSPENDING version for arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-password-abc123
INFO: Set new password on RDS instance
INFO: Test connection successful
INFO: Finished rotation for arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/db-password-abc123
💡Idempotency Is Key
Your Lambda should handle retries gracefully. If createSecret is called twice, it should not create duplicate versions. Use ClientRequestToken to deduplicate.
📊 Production Insight
We once had a race condition where two rotation events overlapped, causing AWSPENDING to be overwritten. Use a distributed lock or check for existing pending version.
🎯 Key Takeaway
Custom rotation Lambda must handle four steps idempotently to ensure zero downtime.
aws-secrets-manager THECODEFORGE.IO Secrets Manager Integration Stack Layered architecture for secure credential management Application Layer Web Server | API Gateway | Lambda Functions Integration Layer AWS SDK | Secrets Manager Client | IAM Roles Secrets Management Secret Versions | Rotation Lambda | Encryption Keys Monitoring Layer CloudWatch Alarms | Rotation Logs | Audit Trails Compliance Layer Multi-Region Replication | Cross-Account Access | KMS Policies THECODEFORGE.IO
thecodeforge.io
Aws Secrets Manager

Integrating Secrets Manager with Your Application

Once secrets are stored and rotated, your application needs to fetch them dynamically. The worst approach is to read the secret once at startup and cache it indefinitely—you'll miss rotations. Instead, use the AWS SDK to fetch the secret on demand or cache with a short TTL (e.g., 5 minutes). For high-throughput applications, consider using a secrets caching library like the AWS Secrets Manager Java Client Caching or the Python-based cache with refresh. The key is to handle rotation gracefully: if a secret is rotated while your app holds a stale connection, you need to detect authentication failures and retry with the new secret. Implement a retry mechanism that re-fetches the secret on failure. Also, ensure your application doesn't start if the secret is missing—fail fast. For containerized environments, inject secrets as environment variables at deployment time, but use a sidecar or init container to refresh them periodically. Avoid storing secrets in volumes or config maps.

app_secrets.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import boto3
import json
from cachetools import cached, TTLCache

cache = TTLCache(maxsize=100, ttl=300)  # 5 minutes

def get_secret(secret_name, region_name='us-east-1'):
    session = boto3.session.Session()
    client = session.client(
        service_name='secretsmanager',
        region_name=region_name
    )
    try:
        response = client.get_secret_value(SecretId=secret_name)
        return json.loads(response['SecretString'])
    except Exception as e:
        print(f"Error fetching secret {secret_name}: {e}")
        raise

@cached(cache)
def get_cached_secret(secret_name):
    return get_secret(secret_name)

# Usage in database connection
def get_db_connection():
    secret = get_cached_secret('prod/myapp/db-password')
    # Use secret to connect
    # If connection fails due to auth, invalidate cache and retry
    try:
        conn = create_connection(secret['username'], secret['password'])
        return conn
    except pymysql.err.OperationalError as e:
        if e.args[0] == 1045:  # Access denied
            cache.pop('prod/myapp/db-password', None)
            secret = get_secret('prod/myapp/db-password')  # Force refresh
            return create_connection(secret['username'], secret['password'])
        raise
Output
Connection successful with new credentials after rotation.
⚠ Don't Cache Secrets Indefinitely
If you cache secrets for longer than the rotation period, your app will use stale credentials and fail. Use short TTLs and handle auth failures by refreshing.
📊 Production Insight
A team cached secrets for 24 hours and rotated every 12 hours—causing a 12-hour window of failures. Set cache TTL to less than half the rotation period.
🎯 Key Takeaway
Fetch secrets dynamically with caching and retry logic to handle rotation seamlessly.

Monitoring and Alerting for Rotation Failures

Rotation can fail silently. Common failure modes: Lambda timeout, insufficient IAM permissions, target service unavailability, or network issues. You need monitoring to detect failures immediately. Enable CloudTrail to log all Secrets Manager API calls. Set up CloudWatch alarms on the Lambda function's error count and duration. Also, monitor the secret's last rotation timestamp—if it exceeds the rotation interval, alert. Use AWS Config rules to ensure rotation is enabled on all secrets. For critical secrets, implement a canary that periodically tests the secret's validity (e.g., a Lambda that tries to authenticate with the current secret). If the canary fails, trigger an incident. Also, log rotation events with context: which secret, which step failed, and the error message. This helps in post-mortem analysis. Finally, have a manual rollback procedure: if rotation fails, you can manually set the secret back to AWSPREVIOUS using the AWS CLI.

monitor-rotation.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Check last rotation timestamp
aws secretsmanager describe-secret --secret-id prod/myapp/db-password --query 'LastRotatedDate'

# CloudWatch alarm for Lambda errors
aws cloudwatch put-metric-alarm \
    --alarm-name rotation-lambda-errors \
    --alarm-description "Alarm when rotation Lambda errors > 0" \
    --metric-name Errors \
    --namespace AWS/Lambda \
    --statistic Sum \
    --period 300 \
    --evaluation-periods 1 \
    --threshold 0 \
    --comparison-operator GreaterThanThreshold \
    --dimensions Name=FunctionName,Value=rotation-lambda \
    --alarm-actions arn:aws:sns:us-east-1:123456789012:rotation-alerts

# Manual rollback to previous version
aws secretsmanager update-secret-version-stage \
    --secret-id prod/myapp/db-password \
    --version-stage AWSCURRENT \
    --move-to-version-id $(aws secretsmanager list-secret-version-ids --secret-id prod/myapp/db-password --query "VersionIdsToStages[?contains(@, 'AWSPREVIOUS')]" --output text)
Output
2024-01-15T12:00:00+00:00
{
"StateValue": "OK",
"MetricName": "Errors",
"Namespace": "AWS/Lambda"
}
🔥Rotate on a Schedule, but Monitor Continuously
Set up alerts for both rotation failures and missed rotations. A missed rotation means your secret is older than policy allows.
📊 Production Insight
We had a rotation Lambda that failed due to a VPC endpoint outage, but no alarm was set—the secret expired and caused a production outage. Now we monitor Lambda errors and last rotation timestamp.
🎯 Key Takeaway
Monitor rotation health with CloudWatch alarms and canary tests to catch failures early.

Handling Multi-Region and Cross-Account Secrets

In multi-region architectures, you need to replicate secrets across regions for disaster recovery. Secrets Manager supports replication via the replicate-secret-to-regions API. However, rotation must be handled carefully: if you rotate a secret in the primary region, the replica becomes stale. You have two options: either rotate in each region independently (with separate Lambda functions) or use a global rotation strategy where the primary region rotates and then pushes the new secret to replicas. The latter is simpler but introduces latency. For cross-account access, use resource-based policies on the secret to grant access to other accounts. For example, a central secrets account can share secrets with multiple workload accounts. Ensure that the rotation Lambda in the central account can update the target service in the workload account (via cross-account IAM roles). This adds complexity but centralizes management. Also, consider using AWS Organizations to automate secret sharing.

replicate-secret.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Replicate secret to another region
aws secretsmanager replicate-secret-to-regions \
    --secret-id prod/myapp/db-password \
    --add-replica-regions Region=eu-west-1

# Cross-account access policy (attached to secret)
aws secretsmanager put-resource-policy \
    --secret-id prod/myapp/db-password \
    --resource-policy '{
        "Version": "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
            "Action": "secretsmanager:GetSecretValue",
            "Resource": "*"
        }]
    }'
Output
{
"ARN": "arn:aws:secretsmanager:eu-west-1:123456789012:secret:prod/myapp/db-password-abc123",
"ReplicationStatus": [{"Region": "eu-west-1", "Status": "InSync"}]
}
⚠ Replica Secrets Are Read-Only
You cannot rotate a replica secret directly. Rotate the primary, then replicate. Or implement independent rotation per region.
📊 Production Insight
A team rotated a secret in us-east-1 but forgot to replicate to eu-west-1, causing failover to use a stale secret. Automate replication after rotation.
🎯 Key Takeaway
Replicate secrets for DR but ensure rotation strategy accounts for multi-region consistency.

Cost Optimization and Secret Organization

Secrets Manager costs $0.40 per secret per month plus $0.05 per 10,000 API calls. For large deployments, costs can add up. Optimize by consolidating related secrets into a single secret with a JSON structure (e.g., all database credentials for one service). Use tags to organize secrets by environment, team, or application. Implement lifecycle policies: delete unused secrets after a retention period (e.g., 30 days). Use AWS Backup to take snapshots of secrets for disaster recovery. Also, consider using Parameter Store for non-sensitive configuration (e.g., feature flags) to reduce costs. For secrets that are rotated frequently, the API call cost can be significant—batch updates if possible. Monitor usage with Cost Explorer and set budgets.

cost-optimization.shBASH
1
2
3
4
5
6
7
8
9
10
# Tag secrets for cost allocation
aws secretsmanager tag-resource \
    --secret-id prod/myapp/db-password \
    --tags Key=Environment,Value=prod Key=Team,Value=platform

# List secrets with no rotation (potential cost waste)
aws secretsmanager list-secrets --filters Key=rotation-enabled,Values=false --query 'SecretList[*].Name'

# Delete unused secret (after ensuring no dependencies)
aws secretsmanager delete-secret --secret-id old/unused-secret --force-delete-without-recovery
Output
["old/unused-secret", "test/expired-secret"]
💡Consolidate Secrets to Reduce Costs
Store multiple key-value pairs in one secret as JSON. This reduces the number of secrets and API calls.
📊 Production Insight
We had 500 secrets for individual API keys—costing $200/month. Consolidated into 50 secrets with JSON, saving 90%.
🎯 Key Takeaway
Organize secrets with tags and consolidate to control costs and improve manageability.

Testing Rotation in a Staging Environment

Never test rotation in production first. Set up a staging environment that mirrors production: same target services, same IAM roles, same network configuration. Use a separate secret (e.g., staging/myapp/db-password) with a short rotation interval (e.g., 1 hour) to test frequently. Automate testing with a script that triggers rotation and validates the outcome: check that the new secret works, the old secret still works (during grace period), and that applications using the secret reconnect without errors. Simulate failure scenarios: make the target service unavailable, revoke Lambda permissions, or corrupt the secret. Verify that the rotation Lambda handles these gracefully and that alerts fire. Also, test rollback: manually set the secret back to AWSPREVIOUS and confirm applications recover. Document the test results and update the rotation Lambda accordingly.

test-rotation.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Trigger rotation manually
aws secretsmanager rotate-secret --secret-id staging/myapp/db-password

# Wait for rotation to complete
sleep 60

# Verify new secret works
aws secretsmanager get-secret-value --secret-id staging/myapp/db-password --version-stage AWSCURRENT

# Verify old secret still works (AWSPREVIOUS)
aws secretsmanager get-secret-value --secret-id staging/myapp/db-password --version-stage AWSPREVIOUS

# Test rollback
aws secretsmanager update-secret-version-stage \
    --secret-id staging/myapp/db-password \
    --version-stage AWSCURRENT \
    --move-to-version-id $(aws secretsmanager list-secret-version-ids --secret-id staging/myapp/db-password --query "VersionIdsToStages[?contains(@, 'AWSPREVIOUS')]" --output text)
Output
{
"ARN": "arn:aws:secretsmanager:us-east-1:123456789012:secret:staging/myapp/db-password-xyz789",
"Name": "staging/myapp/db-password",
"VersionId": "new-version-id",
"SecretString": "{\"username\":\"myapp\",\"password\":\"NewPassword123!\"}",
"VersionStages": ["AWSCURRENT"]
}
🔥Test with a Short Rotation Interval
Set rotation interval to 1 hour in staging to validate the process multiple times a day. Increase to 30 days in production.
📊 Production Insight
We discovered that our Lambda timed out during rotation because the target database was in a different VPC. Staging caught this before production.
🎯 Key Takeaway
Always test rotation in staging with failure scenarios to ensure reliability in production.

Common Pitfalls and How to Avoid Them

Even with careful design, rotation can fail. Common pitfalls: 1) Not handling the grace period—if the target service doesn't support two active passwords, you must update all clients before finalizing. 2) Lambda permissions too broad—use least privilege. 3) Not testing with actual application traffic—a secret may work in isolation but fail under load. 4) Forgetting to update connection strings in application code—use dynamic fetching. 5) Rotating secrets that are used by multiple services simultaneously—coordinate via a distributed lock or stagger rotations. 6) Not monitoring rotation failures—set alerts. 7) Hardcoding secret names in Lambda—use environment variables. 8) Not planning for region failure—replicate secrets. 9) Using default KMS key—use a custom key for better control. 10) Not documenting the rotation process—team members need to know how to respond to failures. Avoid these by following AWS best practices and conducting regular drills.

pitfall_example.pyPYTHON
1
2
3
4
5
6
7
8
9
# Pitfall: Not handling grace period
# If target service doesn't support two passwords,
# you must update all clients before finishSecret.
# Example: Redis password rotation
# Solution: Use a two-phase approach:
# 1. Set new password but keep old one valid
# 2. Update all clients
# 3. Remove old password
# This requires custom logic in setSecret and finishSecret.
Output
ERROR: Clients using old password get 'NOAUTH Authentication required' after rotation.
⚠ Grace Period Is Not Automatic
Not all services support two active passwords. You must design your rotation to handle this, or you'll cause downtime.
📊 Production Insight
A team rotated a Redis password without a grace period—all clients lost access simultaneously. They had to manually update each client, causing 30 minutes of downtime.
🎯 Key Takeaway
Anticipate common pitfalls like grace period handling and permission bloat to avoid production incidents.

Production Rollout Strategy

Rolling out automated rotation to production requires a phased approach. Start with non-critical secrets (e.g., API keys for internal tools) and monitor for a week. Then move to low-impact secrets (e.g., read-only database users). Finally, rotate critical secrets (e.g., admin credentials) during maintenance windows. Use feature flags to enable rotation per secret. Have a rollback plan: if rotation causes issues, you can disable rotation and revert to manual management temporarily. Communicate the rollout to all teams: share the rotation schedule, expected behavior, and incident response procedures. Provide a runbook for handling rotation failures. After rollout, conduct a post-mortem to identify improvements. Also, consider using AWS Secrets Manager's automatic rotation with a custom Lambda that logs all steps to CloudWatch for auditability. Finally, enforce rotation via AWS Config rules: require all secrets to have rotation enabled within 30 days of creation.

rollout-steps.shBASH
1
2
3
4
5
6
7
8
9
10
11
# Phase 1: Enable rotation on non-critical secret
aws secretsmanager rotate-secret --secret-id dev/internal-api-key

# Phase 2: Monitor for 1 week
aws cloudwatch get-metric-statistics --namespace AWS/Lambda --metric-name Errors --dimensions Name=FunctionName,Value=rotation-lambda --start-time 2024-01-01T00:00:00 --end-time 2024-01-08T00:00:00 --period 3600 --statistics Sum

# Phase 3: Enable rotation on critical secret with maintenance window
aws secretsmanager rotate-secret --secret-id prod/myapp/db-password --rotation-window-hours 2

# Rollback if needed: disable rotation
aws secretsmanager cancel-rotate-secret --secret-id prod/myapp/db-password
Output
{
"RotationEnabled": true,
"LastRotatedDate": "2024-01-15T12:00:00+00:00"
}
💡Start with Low-Risk Secrets
Test rotation on non-critical secrets first to build confidence and refine your Lambda before touching production databases.
📊 Production Insight
We rolled out rotation to all secrets at once and caused a cascading failure when a shared Lambda hit API rate limits. Phased rollout would have caught this.
🎯 Key Takeaway
Roll out rotation incrementally with monitoring and rollback plans to minimize risk.

Beyond Passwords: Rotating Other Credential Types

Secrets Manager isn't just for passwords. You can rotate SSH keys, API tokens, OAuth tokens, and even X.509 certificates. For SSH keys, store the private key as the secret and rotate by generating a new key pair and updating authorized_keys on servers. For API tokens, the rotation Lambda must call the third-party API to generate a new token and invalidate the old one. For OAuth tokens, you need to handle refresh tokens and access tokens separately. For certificates, use ACM for automatic renewal, but if you have custom certificate authorities, store the certificate and private key in Secrets Manager and rotate before expiry. Each credential type requires a custom rotation strategy. The key is to understand the lifecycle of the credential: can it be rotated without downtime? Does it support multiple active versions? Always test with the actual service before deploying.

rotate_api_token.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import boto3
import requests

def rotate_api_token(secret_arn):
    client = boto3.client('secretsmanager')
    # Get current token
    current = client.get_secret_value(SecretId=secret_arn)
    old_token = current['SecretString']
    
    # Generate new token via API
    response = requests.post(
        'https://api.example.com/token/refresh',
        headers={'Authorization': f'Bearer {old_token}'},
        json={'grant_type': 'refresh_token'}
    )
    new_token = response.json()['access_token']
    
    # Store new token as AWSPENDING
    client.put_secret_value(
        SecretId=secret_arn,
        SecretString=new_token,
        VersionStages=['AWSPENDING']
    )
    
    # Test new token
    test_response = requests.get(
        'https://api.example.com/me',
        headers={'Authorization': f'Bearer {new_token}'}
    )
    if test_response.status_code != 200:
        raise Exception('New token invalid')
    
    # Finalize
    client.update_secret_version_stage(
        SecretId=secret_arn,
        VersionStage='AWSCURRENT',
        MoveToVersionId=current['VersionId']
    )
Output
Token rotated successfully. New token valid.
🔥Third-Party API Rate Limits
When rotating API tokens, be mindful of rate limits. Implement retries with exponential backoff in your Lambda.
📊 Production Insight
We rotated a GitHub token but hit the API rate limit because the Lambda ran every 30 minutes for multiple secrets. Added a delay and batch rotation.
🎯 Key Takeaway
Secrets Manager can rotate any credential type with a custom Lambda, but each has unique lifecycle considerations.
Manual vs Automated Credential Rotation Comparing manual processes with AWS Secrets Manager automation Manual Rotation AWS Secrets Manager Rotation Frequency Monthly or quarterly Scheduled (e.g., every 30 days) Downtime Risk High (manual updates cause outages) Low (zero-downtime via staged updates) Audit Trail None or manual logs Full CloudTrail and CloudWatch logs Error Handling Manual rollback, slow recovery Automated retries and rollback logic Multi-Region Support Complex and error-prone Built-in replication and failover Cost Efficiency High operational overhead Pay-per-secret, reduced manual effort THECODEFORGE.IO
thecodeforge.io
Aws Secrets Manager

The Future: Secrets Manager with IAM Roles Anywhere and EKS

For workloads outside AWS (e.g., on-premises or other clouds), you can use IAM Roles Anywhere to fetch secrets without long-lived credentials. This extends Secrets Manager's rotation benefits to hybrid environments. For EKS, use the Secrets Store CSI Driver to mount secrets as volumes, with automatic rotation via driver's rotation reconciler. This eliminates the need for application code changes. The driver can sync secrets from Secrets Manager to Kubernetes secrets, and pods get updated automatically. However, be aware of latency: the driver polls for changes every few minutes. For critical secrets, use the driver's rotation with a sidecar that forces refresh. Also, consider using External Secrets Operator for more advanced scenarios. The trend is towards zero-touch secret management where applications don't even know secrets exist—they're injected by the platform.

secrets-store-csi.yamlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: aws-secrets
spec:
  provider: aws
  parameters:
    objects: |
      - objectName: "prod/myapp/db-password"
        objectType: "secretsmanager"
        jmesPath:
          - path: "username"
            objectAlias: "db-username"
          - path: "password"
            objectAlias: "db-password"
  secretObjects:
    - secretName: db-secret
      type: Opaque
      data:
        - objectName: "db-username"
          key: username
        - objectName: "db-password"
          key: password
Output
Pod mounts secret as volume at /mnt/secrets/db-username and /mnt/secrets/db-password
💡Use CSI Driver for EKS to Avoid Code Changes
The Secrets Store CSI Driver mounts secrets as volumes, and the driver handles rotation automatically. No app restart needed.
📊 Production Insight
We migrated from app-level SDK calls to CSI Driver and eliminated secret refresh logic from 20 microservices, reducing code complexity and bugs.
🎯 Key Takeaway
Modern approaches like CSI Driver and IAM Roles Anywhere reduce application coupling to Secrets Manager.
⚙ Quick Reference
11 commands from this guide
FileCommand / CodePurpose
manual-rotation-fail.shDB_PASSWORD="old_password_123"Why Manual Credential Rotation Fails in Production
create-secret.shaws secretsmanager create-secret \AWS Secrets Manager
rotation_lambda.pylogger = logging.getLogger()Designing a Custom Rotation Lambda for Zero Downtime
app_secrets.pyfrom cachetools import cached, TTLCacheIntegrating Secrets Manager with Your Application
monitor-rotation.shaws secretsmanager describe-secret --secret-id prod/myapp/db-password --query 'L...Monitoring and Alerting for Rotation Failures
replicate-secret.shaws secretsmanager replicate-secret-to-regions \Handling Multi-Region and Cross-Account Secrets
cost-optimization.shaws secretsmanager tag-resource \Cost Optimization and Secret Organization
test-rotation.shaws secretsmanager rotate-secret --secret-id staging/myapp/db-passwordTesting Rotation in a Staging Environment
rollout-steps.shaws secretsmanager rotate-secret --secret-id dev/internal-api-keyProduction Rollout Strategy
rotate_api_token.pydef rotate_api_token(secret_arn):Beyond Passwords
secrets-store-csi.yamlapiVersion: secrets-store.csi.x-k8s.io/v1The Future

Key takeaways

1
Automatic Rotation Reduces Risk
Secrets Manager automates credential rotation, eliminating manual errors and ensuring compliance with security policies. Use it for any secret that needs periodic rotation.
2
Understand the Rotation Strategy
Choose between 'single user' and 'alternating users' based on your application's tolerance for downtime. Alternating users is safer for production workloads.
3
Monitor Rotation Failures
Set up CloudWatch alarms on rotation failures. A stuck rotation can leave your app with stale credentials or, worse, no valid credentials at all.
4
Cache Secrets Wisely
Use the AWS Secrets Manager Agent or SDK caching to reduce API calls and latency, but ensure your cache respects the rotation schedule to avoid using expired secrets.

Common mistakes to avoid

2 patterns
×

Overlooking aws secrets manager basic configuration

Symptom
Unexpected behavior in production
Fix
Follow AWS best practices and review documentation thoroughly
×

Ignoring cost implications

Symptom
Unexpected AWS bill at end of month
Fix
Set up billing alerts and use cost explorer to monitor usage
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What is AWS Secrets Manager: Secure Credential Rotation and when would y...
Q02SENIOR
How do you secure AWS Secrets Manager: Secure Credential Rotation in pro...
Q03SENIOR
What are the cost optimization strategies for AWS Secrets Manager: Secur...
Q01 of 03JUNIOR

What is AWS Secrets Manager: Secure Credential Rotation and when would you use it?

ANSWER
AWS Secrets Manager: Secure Credential Rotation is an AWS service that helps manage cloud infrastructure efficiently. Use it when you need scalable, reliable cloud solutions.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
How does Secrets Manager handle rotation for RDS databases?
02
Can I rotate secrets without downtime?
03
What happens if the rotation Lambda fails?
04
How do I rotate secrets for non-RDS services like API keys?
05
What is the cost of using Secrets Manager?
06
How do I replicate secrets across regions?
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 12, 2026
last updated
2,073
articles · all by Naren
🔥

That's AWS. Mark it forged?

7 min read · try the examples if you haven't

Previous
AWS KMS: Key Management and Encryption
26 / 54 · AWS
Next
Amazon Cognito: Authentication and User Management