Automate AWS secrets rotation for RDS, API keys, and credentials
69%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Fix and improve this skill with Tessl
tessl review fix ./skills/antigravity-aws-secrets-rotation/SKILL.mdAutomate rotation of secrets, credentials, and API keys using AWS Secrets Manager and Lambda.
Select the exact account/role, partition, regions and control/framework version before collecting evidence; verify identity with aws sts get-caller-identity. Read-only assessment is the default. Commands that create MFA devices, change policies/keys, enable services, rotate secrets or create alarms require an approved change scope and rollback plan. Keep evidence and secret material out of shared logs.
These are partial examples, not certification or complete benchmark implementations. Control numbers are illustrative historical mappings, not a pinned current benchmark. Record PASS/FAIL/UNKNOWN/NOT-APPLICABLE per control with evidence and coverage. AccessDenied, API errors, missing permissions, absent pages and unimplemented checks are UNKNOWN, never PASS. Use paginators for SDK listings and cover all selected regions/accounts; never infer completeness from no findings. Shell snippets require Bash/GNU date, AWS CLI, jq and error checking (set -euo pipefail in complete scripts). Generate the IAM credential report first, wait for COMPLETE and decode its base64 Content; parse CSV by header in production. Credential reports are sensitive.
Use this skill when you need to implement automated secrets rotation, manage credentials securely, or comply with security policies requiring regular key rotation.
AWS Services
Third-Party Services
Placeholders below are not deployable credentials. Use private input files (--secret-string file://...) rather than inline real secret values visible in shell history/process arguments. Binary output files need umask 077 and exclusive creation; never overwrite a real private key.
# Create RDS secret
aws secretsmanager create-secret \
--name prod/db/mysql \
--description "Production MySQL credentials" \
--secret-string '{
"username": "admin",
"password": "CHANGE_ME",
"engine": "mysql",
"host": "mydb.cluster-abc.us-east-1.rds.amazonaws.com",
"port": 3306,
"dbname": "myapp"
}'
# Create API key secret
aws secretsmanager create-secret \
--name prod/api/stripe \
--secret-string '{
"api_key": "sk_live_xxxxx",
"webhook_secret": "whsec_xxxxx"
}'
# Create secret from file
aws secretsmanager create-secret \
--name prod/ssh/private-key \
--secret-binary fileb://~/.ssh/id_rsa# Get secret value
aws secretsmanager get-secret-value \
--secret-id prod/db/mysql \
--query 'SecretString' --output text
# Get specific field
aws secretsmanager get-secret-value \
--secret-id prod/db/mysql \
--query 'SecretString' --output text | \
jq -r '.password'
# Get binary secret
aws secretsmanager get-secret-value \
--secret-id prod/ssh/private-key \
--query 'SecretBinary' --output text | \
base64 --decode > "$PRIVATE_OUTPUT_FILE" # fresh private path, umask 077# Enable automatic rotation (30 days)
aws secretsmanager rotate-secret \
--secret-id prod/db/mysql \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:SecretsManagerRDSMySQLRotation \
--rotation-rules AutomaticallyAfterDays=30
# Rotate immediately
aws secretsmanager rotate-secret \
--secret-id prod/db/mysql
# Check configuration only; completion requires expected VersionId at AWSCURRENT
# and successful target authentication/consumer refresh.
aws secretsmanager describe-secret \
--secret-id prod/db/mysql \
--query 'RotationEnabled'Use the AWS-maintained rotation template appropriate to the database/service and verify its permissions and network prerequisites. A function must validate SecretId, rotation enabled state, ClientRequestToken membership and AWSPENDING stage before mutation; already-current tokens should return idempotently.
createSecret: reuse an existing pending version for this token or generate/store it once.setSecret: retrieve that exact pending VersionId and AWSPENDING stage; verify current/pending refer to the same authorized host/account/user before changing the target. Never update the database with AWSCURRENT by mistake.testSecret: connect using the pending credentials, bounded timeouts, TLS verification and minimum permissions; close connections on all paths.finishSecret: promote the tested pending version; remove AWSCURRENT from the previous version only as documented. Monitor consumers and retain a recovery path.Do not substitute an asynchronous master-password modification for a working database-user rotation implementation. Application refresh, overlapping credentials and retry/idempotency behavior need integration tests.
Stripe documents creation, expiration and rotation through its Dashboard. This skill does not provide a verified key-management API. Do not call invented /v1/api_keys creation/revocation endpoints or put secret key material in URL paths. Follow the provider's documented workflow, stage the replacement privately in Secrets Manager, update/test consumers, and revoke the old key after confirmed rollout. Webhook signing secrets are a separate credential.
The following is a custom metric contract, not a guaranteed native Secrets Manager metric. Publish RotationFailed in your own namespace from monitored failure events; verify a test failure reaches the alarm and SNS destination. An alarm on a nonexistent series does not detect failures.
# Create alarm for rotation failures
aws cloudwatch put-metric-alarm \
--alarm-name secrets-rotation-failures \
--alarm-description "Alert on secrets rotation failures" \
--metric-name RotationFailed \
--namespace MyApp/SecretRotation \
--statistic Sum \
--period 300 \
--evaluation-periods 1 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--alarm-actions arn:aws:sns:us-east-1:123456789012:alerts#!/bin/bash
# audit-rotations.sh
echo "Secrets Rotation Audit"
echo "====================="
aws secretsmanager list-secrets --query 'SecretList[*].[Name,RotationEnabled,LastRotatedDate]' \
--output text | tr '\t' '\n' | \
while read name enabled last_rotated; do
echo ""
echo "Secret: $name"
echo " Rotation Enabled: $enabled"
echo " Last Rotated: $last_rotated"
if [ "$enabled" = "True" ]; then
# Check rotation schedule
rules=$(aws secretsmanager describe-secret --secret-id "$name" \
--query 'RotationRules.AutomaticallyAfterDays' --output text)
echo " Rotation Schedule: Every $rules days"
# Calculate days since last rotation
if [ "$last_rotated" != "None" ]; then
days_ago=$(( ($(date +%s) - $(date -d "$last_rotated" +%s)) / 86400 ))
echo " Days Since Rotation: $days_ago"
if [ $days_ago -gt $rules ]; then
echo " ⚠️ OVERDUE for rotation!"
fi
fi
fi
doneimport boto3
import json
def get_secret(secret_name):
"""Retrieve secret from Secrets Manager"""
client = boto3.client('secretsmanager')
try:
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
except Exception as e:
print(f"Error retrieving secret: {e}")
raise
# Usage
db_creds = get_secret('prod/db/mysql')
connection = pymysql.connect(
host=db_creds['host'],
user=db_creds['username'],
password=db_creds['password'],
database=db_creds['dbname']
)This is legacy AWS SDK v2 syntax. For a new integration use the installed AWS SDK v3 SecretsManagerClient/GetSecretValueCommand API and its current credential chain. Do not log complete SDK errors or secret responses.
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
async function getSecret(secretName) {
try {
const data = await secretsManager.getSecretValue({
SecretId: secretName
}).promise();
return JSON.parse(data.SecretString);
} catch (err) {
console.error('Error retrieving secret:', err);
throw err;
}
}
// Usage
const dbCreds = await getSecret('prod/db/mysql');
const connection = mysql.createConnection({
host: dbCreds.host,
user: dbCreds.username,
password: dbCreds.password,
database: dbCreds.dbname
});Planning
Implementation
Operations
# Immediate rotation (compromise detected)
aws secretsmanager rotate-secret \
--secret-id prod/db/mysql \
--rotate-immediately
# Force rotation even if recently rotated
aws secretsmanager rotate-secret \
--secret-id prod/api/stripe \
--rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:RotateStripeKey \
--rotate-immediately
# LastRotatedDate alone does not prove this requested rotation completed.
aws secretsmanager describe-secret \
--secret-id prod/db/mysql \
--query 'LastRotatedDate'#!/usr/bin/env python3
# compliance-report.py
import boto3
from datetime import datetime, timedelta
client = boto3.client('secretsmanager')
def generate_compliance_report():
secrets = [s for page in client.get_paginator('list_secrets').paginate() for s in page['SecretList']]
compliant = []
non_compliant = []
for secret in secrets:
name = secret['Name']
rotation_enabled = secret.get('RotationEnabled', False)
last_rotated = secret.get('LastRotatedDate')
if not rotation_enabled:
non_compliant.append({
'name': name,
'issue': 'Rotation not enabled'
})
continue
if last_rotated:
days_ago = (datetime.now(last_rotated.tzinfo) - last_rotated).days
if days_ago > 90:
non_compliant.append({
'name': name,
'issue': f'Not rotated in {days_ago} days'
})
else:
compliant.append(name)
else:
non_compliant.append({
'name': name,
'issue': 'Never rotated'
})
print(f"Compliant Secrets: {len(compliant)}")
print(f"Non-Compliant Secrets: {len(non_compliant)}")
print("\nNon-Compliant Details:")
for item in non_compliant:
print(f" - {item['name']}: {item['issue']}")
if __name__ == "__main__":
generate_compliance_report()kiro-cli chat "Use aws-secrets-rotation to set up RDS credential rotation"
kiro-cli chat "Create a rotation audit report with aws-secrets-rotation"boisenoise/skills-collections
aa13ba9
Also appears in
last in sync Apr 14, 2026
last in sync Apr 14, 2026
last in sync Apr 14, 2026
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.