Last active
September 13, 2023 20:21
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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