Joel Haubold in aws 3 minutes to read

AWSume: AWS Assume Made Awesome

Update: Good news Windows users! You can now use this AWSume script on Windows too.

Here at Trek10, we work with many clients, and thus work with multiple AWS accounts on a regular (daily) basis. We needed a way to make managing all our different accounts easier. We create a standard Trek10 administrator role in our clients’ accounts that we can assume. For security we require that the role assumer have multifactor authentication enabled.

This works fine when accessing these accounts via the AWS console, but quickly becomes cumbersome when using the AWS CLI or SDKs.

The AWS CLI has support for automatically assuming a role based on a profiles you can setup in your ~/.aws/config file. Once you setup the profiles, you can run a command using the profile. Something like this aws s3 ls --profile profile-for-that-one-account. The CLI will prompt you for your mfa token and assume the role for you. It also caches the credentials for you so you don’t have to keep entering your mfa token if you run multiple commands. This works well except for two things, the SDKs don’t look in the CLI credential cache, and you have to specify the profile everytime you run a command (or set the AWS_DEFAULT_PROFILE environment variable).

Another issue I have is that often I do development work inside a virtual machine, and the aws config files aren’t available. I could of course volume in my .aws directory, but it’s a pain to do that for every new VM I setup.

The Solution - AWSume

AWSume

I wrote a script to leverage the fact that the CLI will assume a role and cache the credentials for you. The script reads the credentials from the CLI cache and writes them into environment variables. This way the CLI will default to using them without specifying the profile and the SDKs will also pickup the creds from the environment variables. It also has an option to print the export statements to easily copy them into a VM or SSH session.

Check it out on GitHub!

# ~/.aws/config
[profile internal-admin]
role_arn = arn:aws:iam::<my aws account id>/role/admin-role
mfa_serial = arn:aws:iam::<my aws account id>:mfa/joel
source_profile = joel

[profile client1-admin]
role_arn = arn:aws:iam::<client #1 account id>/role/admin-role
mfa_serial = arn:aws:iam::<your aws account id>:mfa/joel
source_profile = joel

[profile client2-admin]
role_arn = arn:aws:iam::<client #2 account id>/role/admin-role
mfa_serial = arn:aws:iam::<your aws account id>:mfa/joel
source_profile = joel
# ~/.aws/credential

[default]
aws_access_key_id = AKIAIOIEUFSN9EXAMPLE
aws_secret_access_key = wJalrXIneUATF/K7MDENG/jeuFHEnfEXAMPLEKEY

[joel]
aws_access_key_id = AKIAIOSFODNN7EXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

Notice that the source_profile in the config matches the profile in the credentials. To protect against accidents I’ve set my default profile to access keys for our sandbox account.

The script:

#!/bin/bash

if [[ -z $1 ]];
then
  echo Missing profile name
  echo
  echo Usage: . assume profile-name [show]
  echo
else

  aws s3 ls --profile $1 > /dev/null
  if [[ $? -eq 0 ]];
  then

      cachedCreds=`ls ~/.aws/cli/cache/$1*`

      token=$(python -c 'import json,sys,fileinput;
obj=json.loads(fileinput.input().readline());
creds = obj["Credentials"]
print creds["SessionToken"] + ""
' $cachedCreds)

      id=$(python -c 'import json,sys,fileinput;
obj=json.loads(fileinput.input().readline());
creds = obj["Credentials"]
print creds["AccessKeyId"] + ""
' $cachedCreds)

      key=$(python -c 'import json,sys,fileinput;
obj=json.loads(fileinput.input().readline());
creds = obj["Credentials"]
print creds["SecretAccessKey"] + ""
' $cachedCreds)

      if [[ "$2" == "show" ]];
      then
        echo "export AWS_SESSION_TOKEN=$token"
        echo "export AWS_SECRET_ACCESS_KEY=$key"
        echo "export AWS_ACCESS_KEY_ID=$id"
      fi

      export AWS_SESSION_TOKEN=$token
      export AWS_SECRET_ACCESS_KEY=$key
      export AWS_ACCESS_KEY_ID=$id
  fi
fi

Script logic

  • Call aws s3 ls with the profile you specify as a parameter
  • If there is no error use an inline python script to parse the cached credential json file
  • Export the appropriate variables
  • If the show option is provided, print the export statements

This script is known to run in bash and zsh on OSX. YMMV on other operating systems and shells.

Remember, you will need to source the script to get the environment variables to stick. To do this in a typical shell, you need to prepend a . to your command. For convenience I named the script awsume, put the it in /usr/local/bin and set an alias alias awsume='. awsume'.