AWS from zero · Part 1: Foundations · Lesson 4 of 38 · about 35 minutes

The AWS CLI and profiles

Put the AWS command line interface on your own machine and sign it in without a single access key: aws login turns your console sign-in into temporary credentials that refresh themselves. Profiles, which setting wins when several disagree, output formats and --query, and the trap of an old key that quietly takes priority. A real failure on the way: a sign-in that worked and a Region that didn't stick.

By the end you'll know

  • Three ways to sign the AWS CLI in, and why the oldest one, access keys, is the one to avoid
  • How aws login turns your console sign-in into temporary credentials on your own machine
  • What a profile is, where its settings live, and which setting wins when several disagree
  • How to ask for output as a table, text or JSON, and trim it with --query
  • What happens when a session ends, and how to sign out on purpose
  • The trap of an old access key that quietly takes priority, and how to spot it

Before you start

  • You need: the account from Lesson 2, and a Mac, Linux or Windows machine with a browser.
  • Cost: $0. Signing in, and every command in this lesson, only reads.
  • The script: cli-tour.sh from the practice folder's lesson-04. It only reads, only uses the bakery's profile, and never prints an account ID, a key or any other profile on your machine.
  • Where you run things: your own machine's terminal, labelled (Your machine). CloudShell's job is done.
  • Every output below is from a real run on November 9, 2026, on a Mac. Account numbers, session names and a sign-in address are hidden, and the lesson says where.

The short version

  • The AWS CLI (command line interface) is the aws command. Everything you can click in the console, it can do, and scripts can run it.
  • Never sign it in with an access key if you can avoid it. A key lives in a file in your home folder until someone deletes it, and that file is what leaks.
  • aws login signs in through your browser and keeps temporary credentials that refresh every 15 minutes, for up to 12 hours. It needs CLI version 2.32.0 or later.
  • A profile is a named set of settings in ~/.aws/config. Name it, and you can't mix up accounts.
  • Command-line options beat environment variables, which beat the files. aws configure list shows where each setting came from.
  • --output and --query make answers readable, for people and for scripts.

What this lesson costs

PartCost
Installing the AWS CLI$0
Signing in with aws login$0
Read-only commands: who am I, Availability Zones$0
This lesson$0, and nothing created

Three ways to sign the CLI in

The CLI needs credentials, like every other way into AWS. There are three common ways to give it some:

CommandWhat it stores on your machineLastsUse it when
aws configureAn access key and its secret, in ~/.aws/credentialsUntil someone deletes the keyAlmost never. Only for a tool that can't use anything else.
aws loginTemporary credentials from your console sign-in, in ~/.aws/login/cache15 minutes at a time, refreshed for up to 12 hoursYou sign in to the console as a root user, an IAM user, or through federation, like this track's Builder ID account
aws configure sso, then aws sso loginTemporary credentials from IAM Identity CenterHours, set by the companyYour company signs you in through Identity Center

For years, the first tutorial everyone followed was aws configure: paste an access key, done. That key then sits in a plain text file in your home folder, works from anywhere in the world, and never expires. Copy the folder, back it up, push it to GitHub by mistake, and the key goes with it. Lesson 2's leaked-key bills start here.

aws login arrived in November 2025 to end that. It opens your browser, you sign in to the console as you normally would, MFA included, and the CLI gets temporary credentials instead of a key.

Install it, or update it

(Your machine)

BASH
aws --version
OUTPUT
aws-cli/2.25.14 Python/3.12.9 Darwin/25.6.0 exe/x86_64

Two problems in one line. 2.25.14 is older than 2.32.0, so aws login doesn't exist yet. And exe/x86_64 is the version for Intel processors, running on an Apple silicon Mac through Rosetta, Apple's translator: it works, but more slowly.

Update it the way it was installed, or you end up with two copies and whichever comes first in your PATH, the list of folders your shell searches, wins. Check first:

(Your machine)

BASH
which aws
ls -l "$(which aws)"
OUTPUT
/usr/local/bin/aws
lrwxr-xr-x  1 root  wheel  22 Apr  9  2025 /usr/local/bin/aws -> /usr/local/aws-cli/aws

/usr/local/aws-cli/ is AWS's own Mac installer, so update with that installer:

(Your machine)

BASH
cd ~/Downloads
curl -o AWSCLIV2.pkg https://awscli.amazonaws.com/AWSCLIV2.pkg
sudo installer -pkg AWSCLIV2.pkg -target /
aws --version
OUTPUT
installer: Package name is AWS Command Line Interface
installer: Installing at base path /
installer: The install was successful.
aws-cli/2.39.7 Python/3.14.8 Darwin/25.6.0 exe/arm64

The download's progress lines and the password prompt are left out. 2.39.7, and exe/arm64, native this time. If which aws had pointed into a Homebrew folder instead, brew upgrade awscli would be the update. On Linux and Windows, AWS's install guide (opens in a new tab) has the matching installer.

Sign in with aws login

(Your machine)

BASH
aws login --profile juniper-rye

| Part | What it does | |---|---| | aws login | Opens your browser to AWS's sign-in page, then waits for you | | --profile juniper-rye | Saves this sign-in under its own name, separate from anything else on your machine |

Check which account you're signing in to

Your browser may remember another AWS account. On the first try here, the sign-in page opened as a classic IAM user sign-in, with a different account's ID already filled in. Signing in there would have pointed this profile at the wrong account. For a new-sign-up account, choose Change to new experience and sign in with your Builder ID. Then check, below, who the profile really is.

After the browser sign-in, the terminal finishes on its own:

OUTPUT
Attempting to open your default browser. If the browser does not open, open the following URL.
If you are unable to open the URL on this device, run this command again with the '--remote' option.

https://us-east-2.signin.aws.amazon.com/v1/authorize?(the rest of the address removed)


Updated profile juniper-rye to use arn:aws:sts::111122223333:assumed-role/AccountFullAccessRole/(session name removed) credentials.
Use "--profile juniper-rye" to use the new credentials, such as "aws sts get-caller-identity --profile juniper-rye"

The account ID and session name are replaced here, and the sign-in address is cut short. The first time, the CLI also asked "Configure AWS skills and the AWS MCP server for your AI coding agent(s)?" That would give coding agents access to the account. Answer n for now.

Check who the profile is, printing only the identity's kind and name:

(Your machine)

BASH
aws sts get-caller-identity --profile juniper-rye --query Arn --output text | cut -d/ -f1-2 | cut -d: -f6
OUTPUT
assumed-role/AccountFullAccessRole

| Part | What it does | |---|---| | aws sts get-caller-identity | Asks AWS "who am I?" It works for any identity, with no permissions needed | | --query Arn --output text | Keeps only the identity's ARN, as plain text | | cut -d/ -f1-2 \| cut -d: -f6 | Trims the ARN to the kind and name, leaving out the account ID and session name |

The same role the console uses, which is right for a Builder ID account. No access key was created or stored.

Profiles, and which setting wins

A profile is a named set of settings in ~/.aws/config. aws login wrote this one; it looks roughly like this, with the account and session hidden:

INI
[profile juniper-rye]
login_session = arn:aws:sts::111122223333:assumed-role/AccountFullAccessRole/...
region = us-east-2

Use a profile with --profile juniper-rye on each command, or for a whole terminal session:

(Your machine)

BASH
export AWS_PROFILE=juniper-rye

When the same setting comes from several places, the AWS CLI picks in this order, first one wins:

  1. Command-line options, like --profile, --region and --output
  2. Environment variables, like AWS_PROFILE, AWS_REGION and AWS_ACCESS_KEY_ID
  3. Credentials from roles and sign-ins, such as Identity Center
  4. The credentials file, ~/.aws/credentials, where aws configure keeps access keys
  5. The config file, ~/.aws/config

That order is why an old key can quietly win. A forgotten AWS_ACCESS_KEY_ID in your shell, or an old key under the same profile in the credentials file, gets used before your fresh sign-in, and commands land in the wrong account or fail with ExpiredToken. The tour below checks for exactly that.

A tour, and a real failure on the way

cli-tour.sh asks a few read-only questions with the juniper-rye profile:

(Your machine)

BASH
cd ~/Apps/aws-practice/lesson-04
chmod +x cli-tour.sh
./cli-tour.sh

The first run:

OUTPUT
== The CLI
aws-cli/2.39.7
== Who this profile is
  assumed-role/AccountFullAccessRole
== Where its settings come from
  NAME       : VALUE                    : TYPE             : LOCATION
  profile    : juniper-rye              : manual           : --profile
  region     : <not set>                : None             : None
  credential type: login session (aws login), refreshed automatically
  stored access keys for this profile: none
== The same question, three output formats
-- --output table

aws: [ERROR]: An error occurred (NoRegion): You must specify a region. You can also configure your region by running "aws configure".

The other three Availability Zone questions failed with the same error, and the last section came out the same as in the run below.

The sign-in worked, and there's no stored key to worry about, but every Availability Zone question failed. aws configure list says why: region : <not set>. Most AWS services work per Region, so the CLI has to know which one. "Who am I?" worked without one because AWS STS answers globally.

The fix is to store the Region in the profile:

(Your machine)

BASH
aws configure set region us-east-2 --profile juniper-rye

| Part | What it does | |---|---| | aws configure set region us-east-2 | Writes one setting to the config file | | --profile juniper-rye | Into this profile's section only; other profiles aren't touched |

Use your own project's Region; Lesson 2's CloudShell check showed it. Run the tour again:

(Your machine)

BASH
./cli-tour.sh
OUTPUT
== The CLI
aws-cli/2.39.7
== Who this profile is
  assumed-role/AccountFullAccessRole
== Where its settings come from
  NAME       : VALUE                    : TYPE             : LOCATION
  profile    : juniper-rye              : manual           : --profile
  region     : us-east-2                : config-file      : ~/.aws/config
  credential type: login session (aws login), refreshed automatically
  stored access keys for this profile: none
== The same question, three output formats
-- --output table
-----------------------------------------
|       DescribeAvailabilityZones       |
+------------+------------+-------------+
|  us-east-2a|  use2-az1  |  available  |
|  us-east-2b|  use2-az2  |  available  |
|  us-east-2c|  use2-az3  |  available  |
+------------+------------+-------------+
-- --output text
us-east-2a	use2-az1	available
us-east-2b	use2-az2	available
us-east-2c	use2-az3	available
-- --output json (first zone only)
{
    "name": "us-east-2a",
    "id": "use2-az1",
    "state": "available"
}
== Filtering with --query: just the names, one per line
us-east-2a
us-east-2b
us-east-2c
== Another Region than the project's
  us-west-2: denied, explicit deny in a service control policy
  eu-west-1: denied, explicit deny in a service control policy

What each part shows:

  • Where each setting came from. The LOCATION column is the first thing to check when a command uses the wrong account or Region: --profile from the command line, the Region from ~/.aws/config.
  • Three output formats for one question. table is for people, text is for shell scripts (tab-separated, one row per line), and json, the default, keeps the full structure for programs.
  • --query filters the answer before it's printed, using a small language called JMESPath. AvailabilityZones[].ZoneName means "the ZoneName of every zone".
  • Zone names and zone IDs. us-east-2a is a name; use2-az1 is an AZ ID, the same physical place in every account. In a few older Regions, the same name could point to different physical zones in different accounts; AWS says accounts created from November 2025 get the same mapping everywhere. When you compare zones across accounts, use the ID.
  • Other Regions are fenced off. In the new sign-up, the service control policy stops you even looking at other Regions.

Signing out, and what happens after

A session ends on its own after the role's session length, at most 12 hours. To end it now:

(Your machine)

BASH
aws logout --profile juniper-rye
OUTPUT
Removed cached login credentials for profile 'juniper-rye'. Note, any local developer tools that have already loaded the access token may continue to use it until its expiration. Access tokens expire in 15 minutes.

Read the second sentence: a tool that already loaded the credentials keeps them for up to 15 minutes. Short-lived credentials limit how long a copied one stays useful; they don't stop it the instant you sign out.

Then try to use the profile:

(Your machine)

BASH
aws sts get-caller-identity --profile juniper-rye --query Arn --output text 2>&1 | sed -E 's/[0-9]{12}/111122223333/g'
OUTPUT
aws: [ERROR]: Error loading login session token: Unable to load a existing login session for session arn:aws:sts::111122223333:assumed-role/AccountFullAccessRole/(session name removed), Please reauthenticate with 'aws login'.

The sed replaces the account ID; the session name was removed by hand. The error tells you exactly what to do. Sign in again before the next lesson:

(Your machine)

BASH
aws login --profile juniper-rye

On a machine without a browser, like a server you've connected to, aws login --remote prints an address to open on another device and asks you to paste back a code.

In real systems

  • People use Identity Center profiles, one per account and role, set up with aws configure sso. Signing in once covers every account they're allowed into.
  • Programs never sign in like people. A server gets its role's credentials automatically, a Lambda function from its execution role, a pipeline through OIDC (OpenID Connect), Lesson 31. None of them store keys.
  • Long-lived keys get hunted down. Security teams look for them, check when each was last used, and remove them. Where one is truly needed, it's rotated and watched.
  • Profiles are named after accounts and roles, like bakery-prod-readonly, so a command's target is obvious from the command itself.

Spot the problem

Work out what's wrong before you read the explanation.

"I signed in, but I'm in the wrong account." A developer runs aws login --profile bakery, signs in fine, and aws sts get-caller-identity --profile bakery works. But every command lands in an old test account they stopped using a year ago, and some fail with ExpiredToken. What's going on? An old access key under the same profile name in ~/.aws/credentials, or an AWS_ACCESS_KEY_ID left set in their shell, is taking priority over the login session. The credentials file and environment variables come before it in the order above. The fix: run aws configure list --profile bakery and read the TYPE and LOCATION columns, then delete the old key from the credentials file, or unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN. Then check: does aws configure list show the credentials coming from the login session, and does cli-tour.sh say "stored access keys for this profile: none"?

Keep this

CommandDoes
aws --versionShows the version; aws login needs 2.32.0 or later
aws login --profile NAMESigns in through the browser; temporary credentials, no keys
aws sts get-caller-identityWho am I, right now?
aws configure list --profile NAMEWhich settings are used, and where each came from
aws configure set region REGION --profile NAMEStores one setting in a profile
export AWS_PROFILE=NAMEUses a profile for the rest of this terminal session
--output table, text, jsonFor people, for shell scripts, for programs
--query 'List[].Field'Keeps only what you ask for
aws logout --profile NAMERemoves the cached credentials; copies already loaded last up to 15 minutes

Where you are now

Your own machine can work with the account, signed in through your browser and MFA, with credentials that refresh themselves and expire on their own, and no access key anywhere. You know where a profile's settings live, which setting wins when several disagree, how to make the CLI's answers readable, and how to spot an old key taking priority. From here on, most hands-on work runs on your machine. Next, Lesson 5 is about Regions and shared responsibility: where your resources live, and which security jobs are AWS's and which are yours.

Check yourself

  1. What does aws login store on your machine?

  2. --profile on the command line says one account, and AWS_PROFILE says another. Which wins?

  3. Commands fail with NoRegion, but get-caller-identity works. Why?

  4. Which output format suits a shell script best?

  5. You compare a zone in your account with a colleague's account. What should you compare?

  6. After aws logout, how long can a tool that already loaded the credentials keep using them?

  7. A login works, but commands land in an old account. What's the likely cause?

Key terms

  • AWS CLI (command line interface): the aws command, for anything you can do in the console.
  • Access key: a long-lived credential for programs, stored in ~/.aws/credentials by aws configure. Avoid it.
  • aws login: signs the CLI in with your console sign-in and caches temporary credentials. Needs version 2.32.0 or later.
  • aws configure sso: sets up sign-in through IAM Identity Center, for company accounts.
  • Profile: a named set of settings in ~/.aws/config, chosen with --profile or AWS_PROFILE.
  • Precedence: the order the CLI picks settings in: command-line options, environment variables, then the files.
  • --output: table, text or json.
  • --query: a JMESPath expression that keeps only part of the answer.
  • AZ ID: an Availability Zone's ID, like use2-az1, the same physical place in every account.
  • PATH: the list of folders your shell searches for commands; the first match wins.

Go deeper