Security and IAM

AWSume: AWS Assume Made Awesome

Joel Haubold Trek10
Joel Haubold | Apr 02 2016

Sat, 02 Apr 2016

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 AWSDEFAULTPROFILE 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


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.

<pre><code># ~/.aws/config[profile internal-admin]role_arn = arn:aws:iam::<my aws account id>/role/admin-rolemfa_serial = arn:aws:iam::<my aws account id>:mfa/joelsource_profile = joel[profile client1-admin]role_arn = arn:aws:iam::<client #1 account id>/role/admin-rolemfa_serial = arn:aws:iam::<your aws account id>:mfa/joelsource_profile = joel[profile client2-admin]role_arn = arn:aws:iam::<client #2 account id>/role/admin-rolemfa_serial = arn:aws:iam::<your aws account id>:mfa/joelsource_profile = joel</code></pre><pre><code># ~/.aws/credential[default]aws_access_key_id = AKIAIOIEUFSN9EXAMPLEaws_secret_access_key = wJalrXIneUATF/K7MDENG/jeuFHEnfEXAMPLEKEY[joel]aws_access_key_id = AKIAIOSFODNN7EXAMPLEaws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY</code></pre>

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:

<pre><code>#!/bin/bashif [[ -z $1 ]];then echo Missing profile name echo echo Usage: . assume profile-name [show] echoelse 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 fifi</code></pre>

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'.