AWS Security Blog

How to Use a Single IAM User to Easily Access All Your Accounts by Using the AWS CLI

Many AWS customers keep their environments separated from each other: development resources do not interact with production, and vice versa. One way to achieve this separation is by using multiple AWS accounts. Though this approach does help with resource isolation, it can increase your user management because each AWS account can have its own AWS Identity and Access Management (IAM) users, groups, and roles.

All programmatic access to your AWS resources takes place via an API call, and all API calls must be signed for authentication and authorization. To sign an AWS API call, you need AWS access keys. Therefore, having multiple users across AWS accounts also can pose a challenge because more users can result in maintaining more AWS access keys. Furthermore, it’s important that you protect them. One way of reducing the number of credentials to manage is to leverage temporary AWS security credentials. You can do this by using AWS Security Token Service (STS) and IAM roles.

To use an IAM role, you have to make an API call to STS:AssumeRole, which will return a temporary access key ID, secret key, and security token that can then be used to sign future API calls. Formerly, to achieve secure cross-account, role-based access from the AWS Command Line Interface (CLI), an explicit call to STS:AssumeRole was required, and your long-term credentials were used. The resulting temporary credentials were captured and stored in your profile, and that profile was used for subsequent AWS API calls. This process had to be repeated when the temporary credentials expired (after 1 hour, by default).

Today, even though the actual chain of API calls is still necessary, the AWS API automates this workflow for you. With a simple setup, you can achieve secure cross-account access with the AWS CLI by simply appending a suffix to your existing AWS CLI commands.

In this blog post, I will show how easy it is to use a single IAM user and the AWS CLI to access all your AWS accounts.

Scenario

Let’s assume that you have two accounts, Dev and Prod. You want to give IAM users in the Dev account limited access to the Prod account via the AWS CLI. This keeps user management in the Dev account and long-term credentials out of the Prod account. To avoid making costly mistakes in your Prod account, the default CLI should leverage an IAM user in your Dev account. As shown in the following diagram, to achieve this you will use an IAM role, which has the access needed in your Prod account. An authenticated user in your Dev account will assume a privileged IAM role in the Prod account with an API call to STS:AssumeRole. This API call will return temporary security credentials that the Dev user’s AWS CLI will automatically use to create or modify resources in the Prod account.

Diagram of the solution

Default behavior

It is important to understand how the AWS CLI handles authentication out of the box because this is what developers will be using for most of their day-to-day activity. After you have installed the AWS CLI, you will need to set up your default credentials. To set up your default CLI credentials, you should gather the AWS access key and secret key for your Dev user, and then run the aws configure command. You will be prompted for 4 inputs (replace the placeholder keys with your user’s keys).

AWS access key ID [None]: <YOUR_AWS_ACCESS_KEY>
AWS secret access key [None]: <YOUR_AWS_SECRET_KEY>
Default region name [None]: us-west-1
Default output format [None]: json

The AWS CLI organizes configuration and credentials into two separate files found in the home directory of your operating system. They are separated to isolate your credentials from the less sensitive configuration options of region and output.

# ~/.aws/config

[default]
region = us-west-1
output = json
# ~/.aws/credentials

[default]
aws_access_key_id = <YOUR_AWS_ACCESS_KEY>
aws_secret_access_key = <YOUR_AWS_SECRET_KEY>

As you can see, the CLI has created these two files and identified them with [default], which indicates that unless otherwise specified, these credentials will be used for any given CLI call.

Profiles

The “magic” behind the CLI’s ability to assume a role is the use of named profiles. You can easily create profiles in your configuration and credentials file by using the aws configure set command:

aws configure set profile.example.aws_access_key_id myid
aws configure set profile.example.aws_secret_access_key mysecret
aws configure set profile.example.region us-west-1

This results in the following.

# ~/.aws/config

[default]
region = us-west-1
output = json

[profile example]
region = us-west-1

# ~/.aws/credentials

[default]
aws_access_key_id = <YOUR_AWS_ACCESS_KEY>
aws_secret_access_key = <YOUR_AWS_SECRET_KEY>	

[example]
aws_access_key_id = myid
aws_secret_access_key = mysecret

Using aws configure will create the sections for you, if they do not already exist.

Credential providers

As noted in the AWS CLI documentation, the CLI uses a provider chain to look for AWS credentials in a number of different places, including system or user environment variables and local AWS configuration files.

The AWS CLI will look for credentials on each call in this order: command-line options, environment variables, AWS credentials file, CLI configuration file, and instance profiles. For the credentials or configuration settings matched first—in the order just mentioned—the credentials or configurations settings are returned and used for that call.

Delegation

Now that you understand how the AWS CLI works, you can apply that model for cross-account calls. As I said before, let’s assume that you already have two accounts called Dev and Prod. You can anticipate that most of the day-to-day development and activity happens in the Dev account, so this will be where the individual IAM user credentials are created and issued. The Prod account will be the one to which secure access is established from the privileged users in the Dev account.

After the accounts are established, perform three tasks:

  1. Create an IAM role in your Prod account.
  2. Create a user in your Dev account to assume that IAM role.
  3. Establish cross-account trust and access from the user in the Dev account to the role in the Prod account.

Task 1: Create an IAM role in your Prod account (the account that users want to sign in to)

You first will need to create an IAM role in your Prod account with a user that has privileged access to do so. This role will need with it a trust policy, which specifies who is allowed to assume the associated role. (Replace the placeholder ID with your own Dev account ID.)

prod_trust_policy.json

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::Dev-account-ID:user/bjwagner"
      },
    "Action": "sts:AssumeRole"
  }]
}

With the trust policy as defined, you can create the role.

aws iam create-role 
  --role-name CrossAccountPowerUser 
  --assume-role-policy-document file://./prod_trust_policy.json 
  --profile prod

Running this in your terminal will produce some information about your role, including the Amazon Resource Name (ARN), which you should take note of before moving on to Task 2. The ARN should look like: “AWS”: “arn:aws:iam::Prod-account-ID:role/CrossAccountPowerUser”.

By default, IAM resources (such as roles) are created without any permissions, so you need to define what this role is capable of doing when it is assumed by attaching a policy. Attach the ReadOnlyAccess managed policy.

aws iam attach-role-policy 
  --role-name CrossAccountPowerUser 
  --policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess 
  --profile prod

Task 2: Create a user in the Dev account with permission to assume the IAM role in the Prod account

Now that you have an appropriate IAM role in place, create a policy that allows its principal to assume the role in Prod. Using the ARN returned from Task 1 as the Resource, the policy looks like the following.

dev_assume_role_prod.json

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": "sts:AssumeRole",
        "Resource": "arn:aws:iam::Prod-account-ID:role/CrossAccountPowerUser"
    }
}

The trust policy in Task 1 only allowed the IAM user bjwagner from the Dev account to assume the role, so you will use create-policy to create an IAM managed policy that you will associate with an IAM user.

aws iam create-policy 
  --policy-name ProdAccountAccess 
  --policy-document file://./dev_assume_role_prod.json

Notice that you didn’t use the –profile option. This is because without that option, the CLI will use the default credentials that were defined with aws configure, which should be configured to use the Dev account.

Upon success, this will return some information about the newly created policy. You will need to take note of the ARN that is part of the output. If you are using JSON, your output format will look similar to the following.

{
    "Policy": {
        "PolicyName": " ProdAccountAccess",
        "CreateDate": "2015-11-10T15:01:32.772Z",
        "AttachmentCount": 0,
        "IsAttachable": true,
        "PolicyId": "ANPAKSKWUJMXAERMQUNK",
        "DefaultVersionId": "v1",
        "Path": "/",
        "Arn": "arn:aws:iam::Dev-account-ID:policy/ProdAccountAccess",
        "UpdateDate": "2015-11-10T15:01:32.772Z"
    }
}

Using the resulting ARN, your last step for this task is to associate the newly created policy with the IAM user in the Dev account. This is achieved with the use of attach-user-policy command.

aws iam attach-user-policy 
  --user-name bjwagner 
  --policy-arn arn:aws:iam::Dev-account-ID:policy/ProdAccountAccess

If nothing is returned, the operation was successful. At this point you have established the permissions needed to achieve cross-account access, and now must configure the AWS CLI to utilize it.

Task 3: Establish cross-account trust and access from the user to the role

Now set the AWS CLI to leverage these changes. To create a profile that will use the role in your Prod account, first apply it to your configuration.

aws configure set profile.prod.role_arn arn:aws:iam::Prod-account-ID:role/CrossAccountPowerUser

aws configure set profile.prod.source_profile default

The first command will create a new CLI profile called prod and will append the given role_arn to ~/.aws/config. The second command sets the source_profile, which references the default credentials profile so that you can use the same IAM user for Dev and Prod.

Your ~/.aws/config file will look like the following.

# ~/.aws/config

[default]
region = us-west-1
output = json

[profile prod]
role_arn = arn:aws:iam::Prod-account-ID:role/CrossAccountPowerUser
source_profile = default

And the ~/.aws/credentials file will remain the same.

# ~/.aws/credentials

[default]
aws_access_key_id = <YOUR_AWS_ACCESS_KEY>
aws_secret_access_key = <YOUR_AWS_SECRET_KEY>

Excercising your power

With this method, you not only keep your Prod account secure by not permitting long-term credentials, but you also manage IAM users by keeping your users in one account. So what does your workflow look like now?

Without specifying the –profile option in your AWS CLI command, you will automatically use the default profile, which is configured to interact with your Dev account using the long-term credentials that were input when calling aws configure.

aws ec2 describe-instances --region us-west-1

This should return your Amazon EC2 resources in your Dev account in the US West (N. California) region. With the addition of –profile prod in the same command, you should get a very different result set back, which are your EC2 resources from the Prod account in US West (N. California).

aws ec2 describe-instances --region us-west-1 --profile prod

By simply appending –profile prod to your command, you have told the AWS CLI to use the named profile prod, which is configured for an IAM role. The CLI will automatically make an STS:AssumeRole call and store the resulting temporary credentials in the ~/.aws/cache file. All future calls made using the same named profile will use the cached temporary credentials until they expire. When the credentials do expire, the AWS CLI will automatically repeat the process to give you fresh credentials.

Conclusion

Not only does leveraging the AWS CLI’s ability to assume roles across accounts address some IAM best practices, it also gives you a central way to manage your user credentials and protect your production environment.

If you are interested in a similar concept that uses the AWS Management Console instead, see the IAM documentation about Switching to a Role (AWS Management Console).

If you have questions or comments, submit them below or on the IAM forum.

– Brian