Managing Multiple AWS Accounts with CLI Profiles: A Practical Guide

Running work and personal AWS accounts from the same machine is a common reality for engineers — and without a proper profile strategy, you risk deploying personal side projects into your company's production account, or worse, billing your employer for your weekend experiments.

TL;DR

StepActionFile Modified
1Add named credentials~/.aws/credentials
2Add named config (region, output)~/.aws/config
3Use --profile flag per commandCLI invocation
4(Optional) Set AWS_PROFILE env varShell session

How AWS CLI Profile Resolution Works

The AWS CLI uses a layered credential resolution chain. Named profiles are stored across two files: ~/.aws/credentials (access keys) and ~/.aws/config (region, output format, and advanced settings). The default profile is used when no profile is explicitly specified. Every other named profile must be referenced explicitly — either via the --profile flag or the AWS_PROFILE environment variable.

graph LR A["AWS CLI Command"] --> B{"--profile flag
or AWS_PROFILE set?"} B -- "Yes" --> C["Load Named Profile
from ~/.aws/config"] B -- "No" --> D["Load 'default' Profile"] C --> E["Read Credentials
from ~/.aws/credentials"] D --> E E --> F["Resolve Region
& Output Format"] F --> G["Sign Request &
Call AWS API"] G --> H["Target AWS Account"]
  1. CLI Command Issued: You run an AWS CLI command, optionally with --profile work.
  2. Profile Lookup: The CLI reads the named profile from ~/.aws/config and ~/.aws/credentials.
  3. Credential Resolution: The matching access key ID and secret are loaded for that profile.
  4. Region & Output: Region and output format are pulled from the profile's config block.
  5. API Call: The request is signed with the resolved credentials and dispatched to the correct AWS account.

Step 1 — Configure Your Credentials File

The ~/.aws/credentials file stores raw access key pairs. Each section header (e.g., [work]) defines a named profile.

# ~/.aws/credentials

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

[work]
aws_access_key_id     = AKIAI44QH8DHBEXAMPLE
aws_secret_access_key = je7MtGbClwBF/2Zp9Utk/h3yCo8nvbEXAMPLEKEY

[personal]
aws_access_key_id     = AKIAIOSFODNN7PERSONAL
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG/bPxRfiCYPERSONALKEY
Analogy: Think of ~/.aws/credentials as a keychain. Each named profile is a labeled key — you pick the right key for the right door (AWS account) without fumbling through all of them every time.

Step 2 — Configure Your Config File

The ~/.aws/config file stores non-secret settings. Note the naming convention difference: the default profile is [default], but all other named profiles use the [profile <name>] prefix — this is a strict AWS CLI requirement.

# ~/.aws/config

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

[profile work]
region = us-east-1
output = json

[profile personal]
region = ap-southeast-1
output = yaml

Step 3 — Using the --profile Flag

Pass --profile <name> to any AWS CLI command to explicitly select which account context to use. This is the safest, most explicit approach — it leaves no ambiguity about which account is being targeted.

# List S3 buckets in your work account
aws s3 ls --profile work

# List EC2 instances in your personal account (ap-southeast-1)
aws ec2 describe-instances --profile personal

# Deploy a CloudFormation stack to your work account
aws cloudformation deploy \
  --template-file template.yaml \
  --stack-name my-app-stack \
  --profile work

Step 4 — Using AWS_PROFILE for Session-Level Switching

If you're running multiple commands in a row against the same account, exporting AWS_PROFILE in your shell session avoids repeating the flag on every command. This is ideal for dedicated terminal windows per account.

# Set the active profile for this shell session
export AWS_PROFILE=work

# All subsequent commands use the 'work' profile implicitly
aws sts get-caller-identity
aws s3 ls
aws ec2 describe-vpcs

# Switch to personal for this session
export AWS_PROFILE=personal
aws sts get-caller-identity

# Unset to fall back to the default profile
unset AWS_PROFILE

Pro tip: Always run aws sts get-caller-identity --profile <name> to verify which account and IAM principal you're operating as before executing any destructive or expensive commands.

# Verify active identity — always do this before critical operations
aws sts get-caller-identity --profile work

# Expected output:
# {
#     "UserId": "AIDACKCEVSQ6C2EXAMPLE",
#     "Account": "111122223333",
#     "Arn": "arn:aws:iam::111122223333:user/work-admin"
# }

Advanced: IAM Role Assumption Across Accounts

For more secure cross-account workflows — especially in enterprise environments — you can configure a profile to automatically assume an IAM role in another account using role_arn and source_profile. This avoids storing long-lived access keys for every account.

🔽 [Click to expand] — Role Assumption Profile Config
# ~/.aws/config

[profile base-user]
region = us-east-1
output = json

[profile work-role]
role_arn       = arn:aws:iam::111122223333:role/DevOpsRole
source_profile = base-user
region         = us-east-1
output         = json

[profile personal-role]
role_arn       = arn:aws:iam::444455556666:role/PersonalAdminRole
source_profile = base-user
region         = ap-southeast-1
output         = yaml

With this setup, running aws s3 ls --profile work-role causes the CLI to first authenticate as base-user, then call sts:AssumeRole to obtain temporary credentials for DevOpsRole in account 111122223333. Temporary credentials are cached locally by the CLI.

sequenceDiagram participant CLI as "AWS CLI" participant Creds as "~/.aws/credentials
(base-user)" participant STS as "AWS STS" participant Service as "AWS Service
(Target Account)" CLI->>Creds: "Read base-user access keys" Creds-->>CLI: "Long-lived credentials" CLI->>STS: "sts:AssumeRole (DevOpsRole ARN)" STS-->>CLI: "Temporary credentials
(AccessKey + SecretKey + SessionToken)" CLI->>Service: "API Call with temporary credentials" Service-->>CLI: "Response"
  1. CLI reads work-role profile: Detects role_arn and source_profile directives.
  2. Authenticates as base-user: Uses long-lived access keys from ~/.aws/credentials.
  3. Calls sts:AssumeRole: Requests temporary credentials for DevOpsRole in the target account.
  4. STS returns temp credentials: Access key, secret, and session token (valid for up to 1 hour by default, configurable).
  5. API call executes: The actual AWS service call is made using the temporary credentials in the target account.

Shell Prompt Integration (Optional but Recommended)

To prevent accidental cross-account operations, display the active AWS profile in your shell prompt. Add the following to your ~/.zshrc or ~/.bashrc:

# Add to ~/.zshrc or ~/.bashrc
prompt_aws_profile() {
  if [ -n "$AWS_PROFILE" ]; then
    echo "[AWS:$AWS_PROFILE] "
  fi
}

# For zsh (add to PROMPT)
PROMPT='$(prompt_aws_profile)'$PROMPT

Security Best Practices

  • Least Privilege: Each profile's IAM user or role should have only the permissions required for its intended workload — avoid using root account credentials or overly broad AdministratorAccess policies for day-to-day CLI use.
  • Prefer Role Assumption: Use role_arn + source_profile over storing multiple long-lived access keys. Temporary credentials reduce the blast radius of a key compromise.
  • Rotate Access Keys: Regularly rotate IAM access keys stored in ~/.aws/credentials. Consider using AWS IAM Identity Center (SSO) for credential vending instead of static keys.
  • File Permissions: Ensure your credentials file is not world-readable: chmod 600 ~/.aws/credentials.

Glossary

TermDefinition
Named ProfileA labeled configuration block in AWS CLI config/credentials files that groups credentials and settings for a specific account or role.
Credential Resolution ChainThe ordered sequence the AWS CLI uses to find credentials: env vars → CLI flags → config files → instance metadata, etc.
STS AssumeRoleAn AWS Security Token Service API call that returns temporary, scoped credentials for an IAM role in any account that trusts the caller.
source_profileA config directive that tells the CLI which base profile's credentials to use when calling sts:AssumeRole for a role-based profile.
AWS_PROFILEAn environment variable that sets the active named profile for all AWS CLI commands in the current shell session.

Next Steps

  • For teams managing many accounts, evaluate AWS IAM Identity Center (formerly AWS SSO) with the aws configure sso command — it eliminates static key management entirely.
  • Official reference: AWS CLI Named Profiles Documentation
  • For automated pipelines, prefer IAM roles attached to compute (EC2 instance profiles, ECS task roles, Lambda execution roles) over any file-based credentials.

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

EC2 SSH Connection Timeout: The Exact Security Group Rules You Need to Fix It

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User