Skip to content

Instantly share code, notes, and snippets.

@stuudmuffin
Last active September 13, 2023 20:21
Show Gist options
  • Save stuudmuffin/7e1f1679ba5718e44283ddac707cbb3d to your computer and use it in GitHub Desktop.
Save stuudmuffin/7e1f1679ba5718e44283ddac707cbb3d to your computer and use it in GitHub Desktop.
If you work in multiple AWS environments, and you want to be sure your keys are rotated every now and then, here is a script that will cycle through your configured accounts, remove extra un-used keys, generate new keys, configure your ~/.aws/credentials with the new keys, and delete the old keys from that particular AWS account.
#!/bin/bash
# Script to rotate your aws keys, and update them in ~/.aws/credentials
# Requirements: awscli, jq
# Will attempt all profiles by default
# You can specify a profile with the -p flag.
# Profiles and their access key/secret can be commented out with # and the script will skip them
AWS_PROFILES=()
for PROFILE in $(grep "\[" ~/.aws/credentials | grep -v '^\s*#'); do
T_PROFILE=$(echo $PROFILE | sed -rn 's/\[(.*)\]/\1/'p)
AWS_PROFILES+=("$T_PROFILE")
done
# This is for finding profiles that use the same keys
# Since bash doesn't support multidimensional arrays, lets store the key and secret in two different arrays
cRecordOfKeys=[]
cRecordOfSecrets=[]
# Usage: ./aws_key_cycle.sh
# -p can be used to rotate just one profile.
while getopts ":p:" opt; do
case ${opt} in
p )
AWS_PROFILES=()
AWS_PROFILES=("$OPTARG")
;;
\? )
echo "Usage: ./aws_key_cycle.sh [-p profile]" 1>&2
exit 1
;;
: )
echo "Usage: ./aws_key_cycle.sh [-p profile]" 1>&2
exit 1
;;
esac
done
shift $((OPTIND -1))
# list all profiles if more than one is to be worked on
if [[ "${#AWS_PROFILES[@]}" -gt 1 ]]; then
echo "All profiles:"
for PROFILE in ${AWS_PROFILES[@]}; do
echo " $PROFILE"
done
fi
for PROFILE in ${AWS_PROFILES[@]}
do
echo "Doing $PROFILE"
cCurrentKey=$(aws --profile=${PROFILE} configure get aws_access_key_id)
cCurrentSec=$(aws --profile=${PROFILE} configure get aws_secret_access_key)
cUserName=$(aws --profile=${PROFILE} iam get-user | jq -r '.User.UserName')
for value in $cRecordOfKeys; do
if [[ " ${value} " == " ${cCurrentKey} " ]]; then
cDupKey="yes"
fi
done
if [[ "$cCurrentKey" == "" ]]; then
echo " No key found for profile: ${PROFILE}"
elif [[ " ${cDupKey} " =~ " yes " ]]; then
cDupKey="no"
echo "current key $cCurrentKey"
echo "all record of keys ${cRecordOfKeys[@]}"
echo "current record of keys ${cRecordOfKeys[$cCurrentKey]}"
echo " This is a duplicate profile. Using key's from previously worked duplicate."
cSetKey=$(aws --profile ${PROFILE} configure set aws_access_key_id ${cRecordOfKeys[$cCurrentKey]})
cSetID=$(aws --profile ${PROFILE} configure set aws_secret_access_key ${cRecordOfSecrets[$cCurrentKey]})
echo " Verifying new key is active"
cVerify=$(aws --profile=${PROFILE} iam get-user | jq -r '.User.UserName')
if [[ "$cUserName" == "$cVerify" ]]; then
echo " Verified! Old key should have been removed preivously. Moving on."
else
echo " New key failed to work for some reason. You must manually check this."
echo " Previous AccessKeyId:${cCurrentKey} Previous SecretAccessKey:${cCurrentSec}"
exit
fi
else
cExistingKeys=$(aws --profile=${PROFILE} iam list-access-keys --user-name ${cUserName})
cEKCount=$(echo ${cExistingKeys} | jq -c -r '.AccessKeyMetadata | length')
if [[ "${cEKCount}" == "1" ]]; then
echo " Only one key found. Easy."
else
echo " Multiple Keys Found. Lets get down to business."
echo ${cExistingKeys} | jq -c -r '.AccessKeyMetadata | .[]' | while read i; do
iKey=$(echo $i | jq -r '.AccessKeyId')
if [[ "$iKey" != "$cCurrentKey" ]]; then
echo " removing extra unused key: $iKey (AWS has a two key limit)"
aws --profile=${PROFILE} iam delete-access-key --access-key-id ${iKey} --user-name ${cUserName}
fi
done
fi
cNewKeyInfo=$(aws --profile=${PROFILE} iam create-access-key --user-name ${cUserName})
cNewKeysID=$(echo $cNewKeyInfo | jq -r '.AccessKey | .AccessKeyId')
cNewSecret=$(echo $cNewKeyInfo | jq -r '.AccessKey | .SecretAccessKey')
cRecordOfKeys[$cCurrentKey]="${cNewKeysID}"
cRecordOfSecrets[$cCurrentKey]="${cNewSecret}"
cSetKey=$(aws --profile ${PROFILE} configure set aws_access_key_id ${cNewKeysID})
cSetID=$(aws --profile ${PROFILE} configure set aws_secret_access_key ${cNewSecret})
echo " Waiting 10 seconds for new key to activate"
sleep 10
cVerify=$(aws --profile=${PROFILE} iam get-user | jq -r '.User.UserName')
if [[ "$cUserName" == "$cVerify" ]]; then
echo " Change success! Removing old key"
aws --profile=${PROFILE} iam delete-access-key --access-key-id ${cCurrentKey} --user-name ${cUserName}
else
echo " Change didn't work for some reason. You must manually fix this."
echo " Previous AccessKeyId:${cCurrentKey} Previous SecretAccessKey:${cCurrentSec}"
exit
fi
fi
done
echo Done
echo .
# AWS CLI helper to change your passwords
generate_password() {
# Define character classes
upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
lower=abcdefghijklmnopqrstuvwxyz
digit=00112233445566778899
special='!!!@#$%^^^&*()____+----.....='
# Generate a string with 2 - 5 special characters
index=$((RANDOM % 4 + 2))
gen_special=""
for i in $(seq 1 10); do
gen_special="$gen_special$(echo "$special" | tr -d ' ' | fold -w 1 | shuf | tr -d '\n' | head -c 1)"
done
use_special=${gen_special:0:$index}
# Generate a string with 2-7 numbers
index=$((RANDOM % 6 + 2))
digit=$(head /dev/urandom | base64 | tr -dc '0-9' | fold -w 24 | head -n 1)
use_digit=${digit:0:$index}
# Count the characters in the two strings
count_specialdigit=$(echo "$use_special$use_digit" | wc -c | tr -d ' ')
# Subtract this from our password minimum length of 22, minus an additional 2.
# since the letters will be generated together, we'll pad the end with an extra
# single upper and single lower.
lower_index=$(( 22 - 2 - count_specialdigit ))
index=$((RANDOM % 5 + $lower_index))
letters=$(head /dev/urandom | base64 | tr -dc 'a-zA-Z' | fold -w 24 | head -n 1)
upper=$(head /dev/urandom | base64 | tr -dc 'A-Z' | fold -w 1 | head -n 1)
lower=$(head /dev/urandom | base64 | tr -dc 'a-z' | fold -w 1 | head -n 1)
use_letters="${letters:0:$index}$upper$lower"
local raw_password=$(echo "$use_letters$use_digit$use_special" | fold -w1 | shuf | tr -d '\n')
local escaped_password=$(echo "$raw_password" | sed 's/[!$^&*()=]/\\&/g')
local result=("$raw_password" "$escaped_password")
echo "${result[@]}"
}
echo "If you'd like you change your passwords as well, here are some aws cli commands to do so"
for PROFILE in ${AWS_PROFILES[@]}
do
cUserName=$(aws --profile=${PROFILE} iam get-user | jq -r '.User.UserName')
password=($(generate_password))
echo "aws iam update-login-profile --profile ${PROFILE} --user-name ${cUserName} --password ${password[1]}"
if [ "${password[0]}" != "${password[1]}" ]; then
echo " Password without escapes: ${password[0]}"
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment