# Authenticating to AWS with the Credentials File

Yevgeniy Brikman, Co-Founder

August 4, 2018

This is Part 1 of the [Comprehensive Guide to Authenticating to AWS on the Command Line](https://blog.gruntwork.io/a-comprehensive-guide-to-authenticating-to-aws-on-the-command-line-63656a686799). In the [intro to the series](https://blog.gruntwork.io/a-comprehensive-guide-to-authenticating-to-aws-on-the-command-line-63656a686799), we went over the basics of AWS Authentication, including IAM Users, IAM Roles, and Access Keys. In this post, we’re going to present the first option for authenticating to AWS on the Command Line: the Credentials File.

## Basic usage

You can store your AWS Access Keys in a _Credentials File_ which lives in `~/.aws/credentials` (or `%UserProfile%\.aws\credentials` on Windows). Normally, the way you create this file is by [installing the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/installing.html) and running the `aws configure` command:

```javascript
$ aws configure
AWS Access Key ID: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json
```

AWS prompts you to enter your Access Key ID and Secret Access Key and stores them in `~/.aws/credentials`:

```haskell
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
```

It also stores the other settings you entered in `~/.aws/config`:

```haskell
[default]
region=us-west-2
output=json
```

Once these files exist, you can run any CLI or SDK tool that talks to AWS, and it will automatically find and use this credentials file and settings.

## Working with multiple sets of Access Keys

If you have multiple sets of Access Keys (e.g., for multiple IAM Users in different AWS accounts), you can create a separate _Named Profile_ for each one in your Credentials File in `~/.aws/credentials`:

```haskell
[default]
aws_access_key_id=AKIAIOSFODNN7EXAMPLE
aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY

[user2]
aws_access_key_id=AKIAI44QH8DHBEXAMPLE
aws_secret_access_key=je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY
```

And similarly, you can have multiple Named Profiles in your Config File in `~/.aws/config`:

```haskell
[default]
region=us-west-2
output=json

[profile user2]
region=us-east-1
output=text
```

Note that the Named Profile called `default` is used, as you might guess, by default. To tell your CLI tools to use something other than the `default` profile, you have to do one of the following:

1. Set the `AWS_PROFILE` environment variable. For example, in Linux, you’d run `export AWS_PROFILE=user2`. After that, you can run any AWS CLI tool (e.g., `terraform apply`), and it should use your Named Profile.
2. Some tools let you specify the profile as a command-line parameter or an argument in code. For example, the `aws` CLI lets you specify `--profile`: `aws ec2 describe-instances --profile user2`. In Terraform, you can set the `profile` parameter in a `provider` block:

```ruby
provider "aws" {
  profile = "user2"
}
```

## Working with IAM Roles

If you want to assume IAM Roles — for example, you have an IAM User in the `security` account and want to assume an IAM Role in your `dev` account—you have two options. The first option depends on the CLI tool you’re using. Some CLI tools allow you to specify the IAM Role to assume via a command-line argument or in the code. For example, with Terraform, you can specify the `assume_role` setting in your `provider` configuration:

```ruby
provider "aws" {
  assume_role {
    role_arn = "arn:aws:iam::123456789012:role/dev-full-access"
  }
}
```

The second option is to specify the `role_arn` parameter in your Config File in `~/.aws/config`:

```ruby
[profile dev-full-access]
role_arn = arn:aws:iam::123456789012:role/dev-full-access
```

With either option, the value you specify for `role_arn` is the [Amazon Resource Name (ARN)](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html) of the IAM Role in the dev account, which will have the format:

```ruby
arn:aws:iam::<ACCOUNT_ID>:role/<ROLE_NAME>
```

Next time you run any CLI command that uses the `dev-full-access` Named Profile, the AWS SDK will automatically assume the IAM Role specified in `role_arn`.

## Working with MFA

If you’re using the `aws` CLI, using MFA with a Credentials Profile is fairly straightforward. You just need to add the `mfa_serial` to your Config File in `~/.aws/config`:

```ruby
[profile with-mfa]
mfa_serial = arn:aws:iam::123456789012:mfa/jon-doe
```

The `mfa_serial` parameter should be set to the ARN of your MFA device, which you can get from the [IAM User page](https://console.aws.amazon.com/iam/home?#/users) of the AWS Web Console. It should be of the format:

```ruby
arn:aws:iam::<ACCOUNT_ID>:mfa/<USERNAME>
```

Once you’ve added the `mfa_serial` parameter, the next time you run the `aws` CLI with that Named Profile, it will prompt you for an MFA token:

```bash
$ aws s3 ls --profile with-mfa

Enter MFA code:
```

But what if you’re running something that isn’t `aws`, such as `terraform` or `packer`? In that case, things get a bit hairy, as most other tools will _not_ automatically prompt you for an MFA token. Instead, you have to do the MFA authentication process outside of that tool, which is a bit of a [tedious process](https://aws.amazon.com/premiumsupport/knowledge-center/authenticate-mfa-cli/).

First, you configure your Credentials File with your normal (permanent) AWS Access Keys (e.g. by running `aws configure`). Next, you run the `aws sts get-session-token` command, passing it the ARN of your MFA device and an MFA token from the Google Authenticator App or your key fob:

```javascript
aws sts get-session-token \
  --serial-number arn:aws:iam::123456789012:mfa/jon-doe \
  --token-code 123456 \
  --duration-seconds 43200
```

This will return a blob of JSON that contains _Temporary Access Keys_ (note the `--duration-seconds` argument in the earlier command, which specifies when these Temporary Access Keys will expire):

```javascript
{
  "Credentials": {
    "SecretAccessKey": "secret-access-key",
    "SessionToken": "temporary-session-token",
    "Expiration": "expiration-date-time",
    "AccessKeyId": "access-key-id"
  }
}
```

You will need to take these Temporary Access Keys and copy them into a Named Profile in your Credentials File in `~/.aws/credentials`:

```ruby
[with-mfa]
aws_access_key_id = <Access-key-as-in-returned-output>
aws_secret_access_key = <Secret-access-key-as-in-returned-output>
aws_session_token = <Session-Token-as-in-returned-output>
```

Note that with Temporary Access Keys, you set not only `aws_access_key_id` and `aws_secret_access_key` in your Credentials File, but also `aws_session_token`.

Now you can run those other CLI tools with this Named Profile:

```bash
export AWS_PROFILE=with-mfa
tf apply
```

Note that the Temporary Access Keys expire after a certain period of time (typically 12 hours), so you’ll have to do this over and over again. Not fun.

## Pros and Cons

### Pros

- Basic authentication is easy.
- Using IAM Roles is easy.
- Named Profiles make it easy to manage multiple sets of credentials and settings.

### Cons

- Your Access Keys are stored in plaintext on disk. **This is NOT secure.**
- You’re always using permanent Access Keys for auth rather than Temporary Access Keys that are rotated.
- Using MFA is complicated and error prone.
- Your credentials sit around on disk forever and the default Named Profile gets used for all commands if you forget to specify a different one, which is error prone.
- It’s tempting to specify Named Profiles in your code (e.g., in your Terraform code) to ensure the proper credentials are used, but that requires all of your team members to use the same names for your Named Profiles, which can be hard to enforce.

## Conclusion

Although many AWS tutorials use the Credentials File, we usually recommend against it, as storing your permanent AWS credentials on disk, in plaintext, is not safe. To make matters worse, MFA usage is so complicated with the Credentials File, that most users don’t bother with it.
