The python based AWS Command Line Interface (CLI) is a unified tool to interact with and manage AWS services programmatically via the terminal. This post will show you a set-up to handling multiple identities scattered across multiple accounts, roles and also sometimes requiring multi-factor authentication (MFA).

Quick set-up

You can install the AWS CLI in a myriad of ways. The thing that might work for most set-ups (depending on how your Python interpreter is set up) is just running pip install awscli. Refer to the installation instructions in the AWS CLI GitHub Repo for more options.

Once installed, getting started is quite easy. Just run aws configure, put in your AWS Access Key Id and Secret Access Key and you are good to go. This will create two INI-formatted files (config and credentials) in the .aws directory of your home folder:

$ ls ~/.aws/
config
credentials

The credentials file will contain a [default] section containing the access key id and secret access key information you put in above:

[default]
aws_access_key_id = ABCDEFGHIJKLMNOP0123
aws_secret_access_key = AEyao7CklmK3Ye7hT5+//KlkjKHGt765

The config file will also contain a [default] section, but this contains the default region and output format you chose during aws configure:

[default]
region = eu-central-1
output = json

If all stays the same these are the default credentials the CLI will use every time you use it (e.g. for listing files in an S3 bucket).

Having multiple identities

Being in an enterprise environment there will come a point where you might have to juggle multiple identities (think multiple accounts or roles). There are a few ways to go about this.

New profiles using aws configure

This simplest way to set up a new identity is to just set up a new profile and provide credentials (in the form of access key id and secret access key) accordingly.

To set up a new profile named prod-account do

aws configure --profile prod-account

and enter the credentials for this account (if you don't have credentials and just a role see below ...).
You should now have a new section [prod-account] in ~/.aws/credentials and also a section [profile prod-account] in ~/.aws/config (the prefix profile is mandatory for all entries except default).

New profiles by directly modifying config and credentials

The same as above can be achieved by directly editing ~/.aws/credentials:

[default]
aws_access_key_id = ABCDEFGHIJKLMNOP0123
aws_secret_access_key = AEyao7CklmK3Ye7hT5+//KlkjKHGt765
[prod-account]
aws_access_key_id = ...
aws_secret_access_key = ...

and ~/.aws/config:

[default]
region = eu-central-1
output = json
[profile prod-account]
region = eu-central-1
output = json

This is basically the gist of it. You need to add a new identity just have your access key id and secret access key ready for each account...

...unless there is another way, without having to juggle countless account logins and secrets.

Roles for granular access

Roles within IAM are a concept to temporarily delegate permissions to anyone (i.e. users) or anything (i.e. ec2 instances, lambdas etc.) that is allowed to use (assume) this role. (see IAM roles terms and concepts)

IAM roles can be used by users of the same account and also by users of a different account. For an in-depth tutorial on cross-account role access see this article in the AWS documentation.

The AWS CLI provides mechanisms, that let you automatically assume a role when performing calls. To leverage this mechanism you will have to edit the ~/.aws/config.

New profiles with session credentials via roles

Let's assume the following:

  • the id of your prod-account is 012345678901
  • the profile prod-account set up above is not allowed to do anything except assuming roles
  • there is a role called NetworkAdminRole that will give you permissions to administer networking (i.e. VPCs, route tables etc.)

We have to tell the CLI it can use this role via a profile. To do this add the profile prod-network-admin to ~/.aws/config:

...
[profile prod-network-admin]
role_arn = arn:aws:iam::012345678901:role/NetworkAdminRole
source_profile = prod-account
region = eu-central-1
output = json

We need to provide the role_arn and source_profile settings which tell the CLI that this profile uses a role that can be accessed through the source profile prod-account (see also this deep-link to some documentation).

Using a role will afford temporary session credentials, that will be cached behind the scenes by the AWS CLI. The time that the session will be valid can be configured via the duration_seconds parameter in the profile config. The default is 3600 (1 hour).

credential_source versus source_profile

Other than providing a source profile inside the profile config you can also specify the option credential_source. credential_source can be Environment, Ec2InstanceMetadata or EcsContainer. Set to Environment the cli will use the current environment's credentials, for example if you provide the environmental variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
The last two are for use in EC2 or ECS respectively.

The options credential_source and source_profile are mutually exclusive!

Using different profiles

All (or at least most of) the commands that you call via the AWS CLI can be told to use a specific profile. You do this, by appending the --profile <profile-name> option to the call:

  • calling S3 list with the default profile:
    aws s3 ls my-bucket
    
  • calling S3 list using the prod-account profile:
    aws s3 ls my-bucket --profile prod-account
    
  • calling S3 list using the prod-network-admin profile:
    aws s3 ls my-bucket --profile prod-network-admin
    

Adding a --profile ... parameter to every call can be quite cumbersome. You can just export the environment variable AWS_PROFILE into your terminal session. This will make the cli to use the specified profile every time:

export AWS_PROFILE=prod-network-admin

### all subsequent aws calls will use the profile prod-network-admin

aws s3 ls my-bucket   

Using multi-factor authentication (MFA) with profiles and the cli

If you (or the company) have a (role) policy in place that only allows programmatic access if there is a multi-factor device enabled you will not have much luck with what we just set up. You will most likely get an AccessDenied error, when performing any operation.

This role can only be assumed, if the assuming user has MFA enabled.:

You need to tell the cli that you have to provide an mfa token. To do that add the mfa_serial option to the profile:

...
[profile prod-network-admin]
role_arn = arn:aws:iam::012345678901:role/NetworkAdminRole
source_profile = prod-account
region = eu-central-1
output = json
mfa_serial = arn:aws:iam::012345678901:mfa/Benjamin.Weigel

You can find the mfa serial (arn) under your security credentials. Got to Username @ Account>My Security Credentials (top right corner in the AWS console).

Once you provide that option the cli will as you to provide an mfa token when you make a request. The session is then cached until it expires:

Using an MFA token running CLI operations.

Wrap-Up

That's it for today. I hope some of you will find this article interesting. I wish I would have known some of this earlier.

Further Reading

TL;DR

Edit ~/.aws/config (and ~/.aws/credentials) to create profiles to use with the AWS CLI (and SDKs etc.). Use role_arn and source_profile to work via roles and avoid having to juggle multiple secrets. Set mfa_serial, when multi-factor authentication is required.
Select which profiles are used by to make calls by adding --profile <profile_name> to cli commands, or set a profile for the current terminal session by specifying the environment variable AWS_PROFILE.