|
#!/bin/bash |
|
|
|
# AWS Credential Rotation Script |
|
# Run this script manually (e.g. quarterly) to rotate your AWS access keys for you. |
|
# Or call it directly from your dev env bootup script (with a --max-age) to automatically |
|
# rotate your credentials when they get too old. |
|
|
|
set -e |
|
|
|
PROFILE="default" # CHANGE IF NEEDED |
|
REGION="us-west-2" # CHANGE IF NEEDED |
|
MIN_AGE_DAYS="" |
|
|
|
# Parse command line arguments |
|
while [[ $# -gt 0 ]]; do |
|
case $1 in |
|
--min-age) |
|
MIN_AGE_DAYS="$2" |
|
shift 2 |
|
;; |
|
--help|-h) |
|
echo "Usage: $0 [--min-age DAYS]" |
|
echo "" |
|
echo "Options:" |
|
echo " --min-age DAYS Only rotate if current key is at least DAYS old" |
|
echo " --help, -h Show this help message" |
|
exit 0 |
|
;; |
|
*) |
|
echo "Unknown option: $1" |
|
echo "Use --help for usage information" |
|
exit 1 |
|
;; |
|
esac |
|
done |
|
|
|
# Check if AWS CLI is installed |
|
if ! command -v aws &> /dev/null; then |
|
echo "Error: AWS CLI is not installed. Please install it with: brew install awscli" |
|
exit 1 |
|
fi |
|
|
|
# Check if profile exists |
|
if ! aws configure list-profiles | grep -q "^${PROFILE}$"; then |
|
echo "Error: AWS profile '${PROFILE}' not found." |
|
echo "Please run: aws configure --profile ${PROFILE}" |
|
exit 1 |
|
fi |
|
|
|
# Test current AWS credentials |
|
CURRENT_USER=$(aws --profile ${PROFILE} sts get-caller-identity --query 'Arn' --output text) || { |
|
echo "Error: Unable to authenticate with current credentials." |
|
echo "Please check your existing AWS configuration." |
|
exit 1 |
|
} |
|
|
|
USERNAME=$(echo $CURRENT_USER | cut -d'/' -f2) |
|
echo "Authenticated as AWS user: ${USERNAME}" |
|
|
|
# Get current access keys |
|
CURRENT_KEYS=$(aws --profile ${PROFILE} iam list-access-keys --user-name ${USERNAME} --query 'AccessKeyMetadata[].AccessKeyId' --output text) |
|
CURRENT_KEY_ID=$(aws --profile ${PROFILE} configure get aws_access_key_id) |
|
|
|
# Calculate key age |
|
KEY_AGE_STR=$(aws --profile ${PROFILE} iam list-access-keys --user-name ${USERNAME} --query "AccessKeyMetadata[?AccessKeyId=='${CURRENT_KEY_ID}'].CreateDate" --output text) |
|
if [[ -z "$KEY_AGE_STR" ]]; then |
|
echo "Error: Could not retrieve creation date for current access key" |
|
exit 1 |
|
fi |
|
|
|
# Calculate key age in days |
|
if [[ "$OSTYPE" == "darwin"* ]]; then |
|
# macOS: Convert +00:00 to +0000 for date command |
|
KEY_AGE_STR_FIXED=$(echo "$KEY_AGE_STR" | sed 's/+00:00/+0000/') |
|
KEY_AGE_DAYS=$(( ($(date +%s) - $(date -j -f "%Y-%m-%dT%H:%M:%S%z" "$KEY_AGE_STR_FIXED" +%s)) / 86400 )) |
|
else |
|
# Linux |
|
KEY_AGE_DAYS=$(( ($(date +%s) - $(date -d "$KEY_AGE_STR" +%s)) / 86400 )) |
|
fi |
|
|
|
echo "Current access key ${CURRENT_KEY_ID} is ${KEY_AGE_DAYS} days old" |
|
|
|
# Check key age if min-age is specified |
|
if [[ -n "$MIN_AGE_DAYS" ]]; then |
|
if [[ $KEY_AGE_DAYS -lt $MIN_AGE_DAYS ]]; then |
|
echo "Skipping AWS key rotation" |
|
exit 0 |
|
fi |
|
|
|
echo "Key is ${KEY_AGE_DAYS} days old so it will now be rotated..." |
|
fi |
|
|
|
# Check if user already has 2 keys (AWS limit) |
|
KEY_COUNT=$(echo $CURRENT_KEYS | wc -w) |
|
if [ $KEY_COUNT -ge 2 ]; then |
|
echo "" |
|
echo "Warning: You already have ${KEY_COUNT} access keys (AWS limit is 2)." |
|
echo "Current keys: $CURRENT_KEYS" |
|
echo "" |
|
# Find the key that's NOT currently configured |
|
for key in $CURRENT_KEYS; do |
|
if [ "$key" != "$CURRENT_KEY_ID" ]; then |
|
OLD_KEY_TO_DELETE="$key" |
|
break |
|
fi |
|
done |
|
read -p "Do you want to delete the old key ${OLD_KEY_TO_DELETE} first and then create a new one? (y/n): " -n 1 -r |
|
echo |
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then |
|
echo "Aborted. Please manually delete an old key first or choose to proceed." |
|
exit 1 |
|
fi |
|
|
|
if [ -n "$OLD_KEY_TO_DELETE" ]; then |
|
echo "Deleting old access key: ${OLD_KEY_TO_DELETE}" |
|
aws --profile ${PROFILE} iam delete-access-key --user-name ${USERNAME} --access-key-id ${OLD_KEY_TO_DELETE} |
|
echo "✅ Old key deleted successfully" |
|
fi |
|
fi |
|
|
|
# Create backup of current credentials |
|
BACKUP_FILE="$HOME/.aws/credentials.backup.$(date +%Y%m%d_%H%M%S)" |
|
echo "" |
|
echo "Creating backup of current credentials at: ${BACKUP_FILE}" |
|
cp "$HOME/.aws/credentials" "${BACKUP_FILE}" |
|
|
|
# Create new access key |
|
echo "" |
|
echo "Creating new access key..." |
|
NEW_KEY_JSON=$(aws --profile ${PROFILE} iam create-access-key --user-name ${USERNAME}) || { |
|
echo "Error: Failed to create new access key. Check your IAM permissions." |
|
echo "You need the following permissions: iam:CreateAccessKey, iam:DeleteAccessKey, iam:ListAccessKeys" |
|
exit 1 |
|
} |
|
|
|
NEW_ACCESS_KEY_ID=$(echo "$NEW_KEY_JSON" | grep -o '"AccessKeyId": "[^"]*"' | cut -d'"' -f4) |
|
NEW_SECRET_ACCESS_KEY=$(echo "$NEW_KEY_JSON" | grep -o '"SecretAccessKey": "[^"]*"' | cut -d'"' -f4) |
|
|
|
if [[ -z "$NEW_ACCESS_KEY_ID" || -z "$NEW_SECRET_ACCESS_KEY" ]]; then |
|
echo "Error: Failed to extract new access key details" |
|
echo "JSON Output: $NEW_KEY_JSON" |
|
exit 1 |
|
fi |
|
|
|
echo "✅ New access key created: ${NEW_ACCESS_KEY_ID}" |
|
|
|
# Update local AWS configuration |
|
echo "" |
|
echo "Updating local AWS credentials..." |
|
aws configure set aws_access_key_id "${NEW_ACCESS_KEY_ID}" --profile ${PROFILE} |
|
aws configure set aws_secret_access_key "${NEW_SECRET_ACCESS_KEY}" --profile ${PROFILE} |
|
aws configure set region "${REGION}" --profile ${PROFILE} |
|
|
|
# Wait a moment for AWS to propagate the new key |
|
echo "Waiting for AWS to propagate new credentials..." |
|
sleep 5 |
|
|
|
# Test new credentials |
|
echo "" |
|
echo "Testing new credentials..." |
|
if aws --profile ${PROFILE} sts get-caller-identity >/dev/null 2>&1; then |
|
echo "✅ New credentials are working correctly!" |
|
|
|
# Delete the old access key |
|
echo "" |
|
echo "Deleting old access key: ${CURRENT_KEY_ID}" |
|
aws --profile ${PROFILE} iam delete-access-key --user-name ${USERNAME} --access-key-id ${CURRENT_KEY_ID} |
|
echo "✅ Old access key deleted successfully!" |
|
|
|
echo "" |
|
echo "🎉 Credential rotation completed successfully!" |
|
echo "" |
|
echo "Summary:" |
|
echo " Old key (deleted): ${CURRENT_KEY_ID}" |
|
echo " New key (active): ${NEW_ACCESS_KEY_ID}" |
|
echo "" |
|
echo "Next rotation due: $(date -v +90d '+%Y-%m-%d')" |
|
|
|
# Clean up backup |
|
echo "" |
|
read -p "Remove credentials backup file (${BACKUP_FILE})? (Y/n): " -n 1 -r |
|
echo |
|
if [[ ! $REPLY =~ ^[Nn]$ ]]; then |
|
rm "${BACKUP_FILE}" |
|
echo "Backup file removed." |
|
else |
|
echo "Backup file preserved at: ${BACKUP_FILE}" |
|
fi |
|
|
|
else |
|
echo "" |
|
echo "❌ New credentials failed to authenticate or timed out." |
|
echo "Restoring previous credentials from backup..." |
|
cp "${BACKUP_FILE}" "$HOME/.aws/credentials" |
|
|
|
# Try to delete the failed new key |
|
echo "Cleaning up failed new access key..." |
|
aws --profile ${PROFILE} iam delete-access-key --user-name ${USERNAME} --access-key-id ${NEW_ACCESS_KEY_ID} 2>/dev/null || true |
|
|
|
echo "Previous credentials restored." |
|
exit 1 |
|
fi |