OnCallReady

Lesson 35.2 · AWS I: CLI, IAM, S3 & KMS · 21 min read

The AWS CLI v2: install, configure, profiles and the credential chain

In plain words

Think of the AWS CLI as a phone that can call any AWS service. Before it dials, it needs to know whose phone card to use. It looks in a fixed order: a name you said out loud just now, then a card someone left in its pocket (environment variables), then the address book (your config files), and so on. It uses the first card it finds, even if that card belongs to someone else.

That is why the first thing to ask when something behaves strangely is not what the command does, but who the phone thinks you are.

The AWS CLI v2

The aws command is how you will touch AWS on call: from your laptop, from a jump box, from a CI job. It is also where most "it works on my machine" incidents start, because the CLI picks its credentials and its Region from half a dozen places, silently. This lesson installs it the way AWS recommends, configures it, and then takes the credential chain apart so that you always know who you are when you press Enter.

Need to know: install CLI v2 with AWS's own installer (/usr/local/bin/aws). aws configure writes keys to ~/.aws/credentials and settings to ~/.aws/config (named profiles are [name] in the first and [profile name] in the second). The CLI looks for credentials in a fixed order and environment variables beat AWS_PROFILE. aws sts get-caller-identity is whoami: run it before anything that matters.

Installing it on Ubuntu 26.04 (arm64)

There are three ways to get aws on this box, and they give you different versions:

HowVersion on 26.04Notes
AWS's installer (awscli-exe-linux-aarch64.zip)2.37.10 (the current release)Bundles its own Python and OpenSSL; installs to /usr/local/aws-cli, links /usr/local/bin/aws
sudo apt install awscli2.31.35Debian's packaging of v2, built on the system Python; months behind upstream
sudo snap install aws-cli --classicfollows upstreamA snap; what Ubuntu's command-not-found suggests first

AWS documents and supports the installer, so the lab uses it. (pip install awscli gives you the old v1 CLI - a different program with different defaults. If aws --version starts with aws-cli/1., that is what you have.)

$ aws --version
Command 'aws' not found, but can be installed with:
sudo apt install awscli
$ cd ~
$ curl -sS "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o awscliv2.zip
$ unzip -q awscliv2.zip
$ ls aws
README.md  THIRD_PARTY_LICENSES  dist  install
$ sudo ./aws/install
You can now run: /usr/local/bin/aws --version
$ aws --version
aws-cli/2.37.10 Python/3.14.6 Linux/7.0.0-31-generic exe/aarch64.ubuntu.26

The version line tells you everything about the binary: CLI version, the bundled Python, the kernel, and exe/aarch64 = the official installer build for arm64. Where it went:

$ which aws
/usr/local/bin/aws
$ readlink -f /usr/local/bin/aws
/usr/local/aws-cli/v2/2.37.10/dist/aws
$ sudo ./aws/install
Found preexisting AWS CLI installation: /usr/local/aws-cli/v2/current. Please rerun install script with --update flag.
$ sudo ./aws/install --update
You can now run: /usr/local/bin/aws --version

The installer refuses to overwrite an existing install unless you say --update - that is how you upgrade later (download the new zip, unzip, sudo ./aws/install --update). AWS also publishes a PGP signature (awscliv2.sig) for the zip; checking it with gpg --verify against AWS's public key is how a careful team proves the download is genuine.

Configuring it: keys, Region, output

aws configure asks four questions and writes the answers to two files:

$ aws configure
AWS Access Key ID [None]: AKIAQ3EGPLAB4LEARNER
AWS Secret Access Key [None]: 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
Default region name [None]: eu-central-1
Default output format [None]: json

Run it again and it shows the current values (the keys masked) in the brackets; Enter keeps them. For scripts there is a non-interactive form, one setting at a time:

$ cd ~/oncall-lab/labs/aws
$ aws configure set aws_access_key_id $(awk '/Access key ID/ {print $NF}' welcome.txt)
$ aws configure set aws_secret_access_key $(awk '/Secret access key/ {print $NF}' welcome.txt)
$ aws configure set region eu-central-1
$ ls -l ~/.aws
total 8
-rw------- 1 learner learner  32 Sep 22 20:00 config
-rw------- 1 learner learner 116 Sep 22 20:00 credentials
$ cat ~/.aws/config
[default]
region = eu-central-1
$ aws sts get-caller-identity
{
    "UserId": "AIDAEVXTZTVCSMBGJ2WDW",
    "Account": "111122223333",
    "Arn": "arn:aws:iam::111122223333:user/learner"
}

Two files, two jobs:

aws sts get-caller-identity answers "who am I?" with the account and the ARN of the principal the CLI signed as. It needs no permission at all - it works even for a principal that is denied everything - so it is always the first command when something is denied.

Profiles

A profile is a named set of credentials and settings. The default profile is used when you name none; others you pick with --profile NAME or export AWS_PROFILE=NAME. The section names differ between the files, a classic typo source:

~/.aws/credentials        ~/.aws/config
[default]                 [default]
[auditor]                 [profile auditor]
$ aws configure set aws_access_key_id $(awk -F= '/KEY_ID/ {print $2}' try/auditor.env) --profile auditor
$ aws configure set aws_secret_access_key $(awk -F= '/SECRET/ {print $2}' try/auditor.env) --profile auditor
$ aws configure set region eu-central-1 --profile auditor
$ aws configure list-profiles
default
auditor
$ aws sts get-caller-identity --profile auditor --query Arn --output text
arn:aws:iam::111122223333:user/try-auditor
$ AWS_PROFILE=auditor aws sts get-caller-identity --query Arn --output text
arn:aws:iam::111122223333:user/try-auditor
$ aws configure get region --profile auditor
eu-central-1

The credential chain

When you run a command, the CLI walks this list and stops at the first place that has credentials (botocore's provider chain):

  1. --profile on the command line - which also switches the environment keys off;
  2. the environment: AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN);
  3. the profile (AWS_PROFILE, else default): role_arn (assume a role) or sso_session (IAM Identity Center), then its keys in ~/.aws/credentials, then keys in ~/.aws/config, then credential_process;
  4. container credentials (ECS tasks, EKS Pod Identity);
  5. the EC2 instance's role, from the instance metadata service.

Step 2 before step 3 is the trap: an export AWS_ACCESS_KEY_ID=... forgotten in a shell (or in ~/.bashrc, or left by a script you sourced) silently beats every AWS_PROFILE you set:

$ source try/auditor.env
$ AWS_PROFILE=default aws sts get-caller-identity --query Arn --output text
arn:aws:iam::111122223333:user/try-auditor
$ aws configure list
NAME       : VALUE                : TYPE        : LOCATION
profile    : <not set>            : None        : None
access_key : ****************PGKK : env         :
secret_key : ****************Jz/g : env         :
region     : eu-central-1         : config-file : ~/.aws/config
$ aws sts get-caller-identity --profile default --query Arn --output text
arn:aws:iam::111122223333:user/learner
$ unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY
$ aws sts get-caller-identity --query Arn --output text
arn:aws:iam::111122223333:user/learner

aws configure list is the tool for this: the TYPE column says where each value came from (env, shared-credentials-file, config-file, assume-role, sso, iam-role) and LOCATION which variable or file. With --profile the CLI logs "Skipping environment variable credential check because profile name was explicitly set" - visible with --debug.

The Region

The Region is resolved separately, in this order: --region, AWS_REGION, AWS_DEFAULT_REGION, the profile's region. With none of them, a regional service refuses:

$ AWS_REGION=eu-west-1 aws configure list
NAME       : VALUE                : TYPE                    : LOCATION
profile    : <not set>            : None                    : None
access_key : ****************RNER : shared-credentials-file :
secret_key : ****************R2Kq : shared-credentials-file :
region     : eu-west-1            : env                     : AWS_REGION
$ aws kms list-keys --profile auditor --region eu-west-1 --query 'length(Keys)'
0
$ aws configure set region '' --profile auditor
$ aws kms list-keys --profile auditor
aws: [ERROR]: You must specify a region. You can also configure your region by running "aws configure".
$ echo $?
253
$ aws configure set region eu-central-1 --profile auditor

Exit code 253 is the CLI's "your configuration is incomplete" - no request was even sent. IAM and STS work without a Region (IAM is global; STS falls back to its global endpoint), which is why get-caller-identity succeeding proves nothing about your Region.

People sign in, they do not carry keys

Long-lived access keys for humans are the thing AWS's own guidance tells you to get rid of. In a real organization people use IAM Identity Center: one sign-in (often through Entra ID or Okta), then a choice of account and permission set, and the CLI gets credentials that expire after a few hours:

$ aws configure sso
SSO session name (Recommended): oncall-lab
SSO start URL [None]: https://oncall-lab.awsapps.com/start
SSO region [None]: eu-central-1
SSO registration scopes [sso:account:access]:
...
The only AWS account available to you is: 111122223333
Using the account ID 111122223333
There are 2 roles available to you.
> AdministratorAccess
  ReadOnlyAccess
...
Profile name [AdministratorAccess-111122223333]: lab-admin

$ aws sso login --profile lab-admin --use-device-code

The profile gets sso_session, sso_account_id and sso_role_name instead of keys; aws sso login refreshes the session (--use-device-code on a machine without a browser: you approve the code on your laptop). The role you land in is AWSReservedSSO_<permission set>_<id>, which is what CloudTrail and AccessDenied messages will show. Since CLI 2.32 there is also aws login, which hands the CLI temporary credentials from your AWS console session (it needs a browser, so it is for laptops, not servers).

In an interview: "How does the CLI decide which credentials to use?" - "It walks a chain: --profile, then the environment variables, then the profile's role, SSO or keys from the credentials and config files, then container and instance-role credentials. The first hit wins. aws configure list shows which one won, and aws sts get-caller-identity shows who that is."

You can now: install and upgrade CLI v2 on arm64 Ubuntu, configure keys and Regions in the right file, use named profiles, predict which credentials the chain picks (and catch an environment variable overriding your profile), and explain why people should use IAM Identity Center instead of access keys.

Why it helps

The single most common surprise with the CLI is that it signed the request as a different identity than you meant: an old environment variable, a default profile, the wrong Region. In production that means changing the wrong account or reading the wrong data with full confidence.

Knowing the credential chain, and having aws configure list and aws sts get-caller-identity as reflexes, removes a whole class of incidents. Profiles, SSO sessions and role profiles are also how every team in a multi-account company works day to day, so this lesson is the base for everything after it.

Commands in this lesson

aws cd curl unzip ls sudo which readlink cat source unset export

FAQ

Why use the official installer instead of apt?

The official installer gives you the current CLI v2 for your CPU (here Linux aarch64) in /usr/local/aws-cli with a symlink in /usr/local/bin, and upgrades with the same script and --update. The Ubuntu package lags behind the AWS releases, so new services and options may be missing. Pick one way per machine; two installs in the PATH confuse everyone.

What is the difference between ~/.aws/config and ~/.aws/credentials?

credentials holds secrets: access key pairs per profile, in sections named [name]. config holds everything else: Region, output format, role and SSO settings, in sections named [profile name] (only the default is [default]). Keeping secrets in one file makes it easy to protect with mode 600 and to keep out of backups and dotfile repositories.

Why do my environment variables beat my profile?

Because the environment comes before the config files in the chain. AWS_PROFILE only chooses which profile the files step uses; if AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set, the files are never reached. Only --profile on the command line skips the environment keys. unset the variables or start a fresh shell.

What does aws configure sso do?

It writes an sso-session and a profile into ~/.aws/config: the Identity Center start URL, the Region, the account and the permission set. aws sso login then opens a browser sign-in and caches a token; the CLI exchanges it for short-lived role credentials whenever it needs them. No access key is stored on disk.

How do I check who I am without changing anything?

aws sts get-caller-identity returns the account, the user ID and the ARN of the identity that signed the request. It needs no permissions and works for every valid credential, so it is the safest first command in any script or session. aws configure list shows where each setting came from.

In an interview Junior

How does the AWS CLI decide which credentials to use?

It walks the credential chain and the first hit wins: --profile on the command line, then the environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN), then the profile from the config files (the one AWS_PROFILE names, or default) with its keys, role or SSO settings, then container and instance-role credentials. The Region is resolved separately the same way. aws configure list shows which source won, and aws sts get-caller-identity shows who that identity is.

Also asked: What is the difference between the config and the credentials file? · How do you use several AWS accounts from one laptop? · Why is IAM Identity Center better than access keys for people?

Practise this lesson in the terminal Free, in your browser - a real Ubuntu terminal to try it in, with missions that check your work.