Using Multiple AWS Accounts on One Computer
9 min read

Using Multiple AWS Accounts on One Computer

Managing more than one AWS account from one computer is a daily need for both software engineers and DevOps — a development account for experiments, staging for testing before releases, production for live systems, or even separate client accounts. Without neat configuration, the biggest risk is real: running destructive commands on the wrong account, for example deleting production resources when you meant staging. AWS CLI provides a profile mechanism to handle this, but just knowing how to create profiles isn’t enough — the part that’s often missed is how to make sure the active profile is truly the one you intend before a command executes. This article discusses AWS CLI profiles thoroughly, from file structure to work habits that prevent fatal mistakes.

AWS Profile Basics

AWS CLI stores configuration in two separate files, and understanding why they’re separated helps avoid confusion when troubleshooting later:

  • ~/.aws/credentials — stores the Access Key and Secret Key, data that’s confidential
  • ~/.aws/config — stores region, output format, and additional settings, data that isn’t confidential

This separation isn’t a coincidence. Credentials are something very sensitive and ideally managed differently from regular settings like region — some teams even separate file system permissions at the OS level between these two files. With profiles, you store many sets of credentials at once on the same computer, then choose which account is active when running a particular command.

flowchart TD
    A[aws CLI command] --> B{Which profile is active?}
    B --> C[Read ~/.aws/credentials]
    B --> D[Read ~/.aws/config]
    C --> E[Access Key + Secret Key for this profile]
    D --> F[Region + output format for this profile]
    E --> G[Request to AWS API]
    F --> G

The credentials File Structure

The ~/.aws/credentials file uses the INI format, with each [profile_name] block representing one account. Suppose you manage three accounts: default, staging, and production.

[default]
aws_access_key_id = AKIADEFAULTEXAMPLE
aws_secret_access_key = secretdefaultkey

[staging]
aws_access_key_id = AKIASTAGINGEXAMPLE
aws_secret_access_key = secretstagingkey

[production]
aws_access_key_id = AKIAPRODEXAMPLE
aws_secret_access_key = secretproductionkey

Several important things about this structure:

  • The [default] block is used automatically if no other profile is explicitly specified — this means a command without any profile flag falls back to this account
  • Profiles other than default are only active when explicitly called, via a flag or environment variable
  • Profile names in the credentials file are written plainly without any prefix: [staging], not [profile staging]
Never put production account credentials as [default]. If one day you run a command and forget to specify a profile without realizing it, that command automatically falls back to default — and if that’s production, a small mistake can be fatal.

The config File Structure

The ~/.aws/config file stores non-secret settings like region and output format per profile. The structure is similar, but there’s one crucial difference that’s often a source of confusion for new developers.

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

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

[profile production]
region = ap-southeast-1
output = json

Look closely: in the config file, every profile other than default must be prefixed with the word profile inside the square brackets — [profile staging], not [staging]. This is the opposite of the credentials file, which uses no prefix at all.

flowchart LR
    subgraph credentials [~/.aws/credentials]
        A1["[default]"]
        A2["[staging]"]
        A3["[production]"]
    end
    subgraph config [~/.aws/config]
        B1["[default]"]
        B2["[profile staging]"]
        B3["[profile production]"]
    end
Forgetting to add the profile prefix in the config file is the most common mistake when setting up multi-account for the first time. The result is that AWS CLI can still authenticate (because the credentials are valid), but the region or output settings for that profile are never read — often appearing as a confusing region error even though the credentials are correct.

Activating a Profile

There are two main ways to determine which profile is active for a command, and both can overlap if used together.

The --profile Flag per Command

The most explicit way — the profile only applies to that single command, without affecting other commands in the same terminal session.

aws s3 ls --profile staging
aws iam list-users --profile production

The AWS_PROFILE Environment Variable

The more practical way if you’ll run many commands in sequence on the same account — set it once, all subsequent commands automatically use that profile until the terminal session closes or it’s changed again.

# macOS / Linux
export AWS_PROFILE=staging

# Windows PowerShell
$Env:AWS_PROFILE="staging"

After this env var is set, all AWS CLI commands in the same terminal session automatically use the staging account without needing to repeat the --profile flag every time.

Precedence Between the Two

If AWS_PROFILE is already set as an env var but you also add the --profile flag to a particular command, the flag always wins for that command alone — the env var doesn’t change, only that command uses a different profile momentarily.

flowchart TD
    A[aws command run] --> B{Has a --profile flag?}
    B -- Yes --> C[Use the profile from the flag]
    B -- No --> D{AWS_PROFILE set?}
    D -- Yes --> E[Use the profile from the env var]
    D -- No --> F[Use default]
You can’t run two active profiles at once in a single command. A profile must be specified per command via the flag, or via an environment variable applying to the whole terminal session — there’s no “multi-profile” mechanism in one execution.

Verifying the Active Profile Before Execution

This is the part most often skipped, even though it’s the most important for preventing the “wrong account” risk mentioned at the start. Knowing how to activate a profile isn’t enough — you also need the habit of verifying which profile is currently active, especially before running destructive commands (delete, terminate, remove-stack, and similar).

The aws sts get-caller-identity command shows the identity of the account currently active based on the credentials being used:

aws sts get-caller-identity --profile production
{
    "UserId": "AIDAEXAMPLE",
    "Account": "111122223333",
    "Arn": "arn:aws:iam::111122223333:user/budi"
}

The Account field here is the most reliable source of truth — not an assumption from the profile name you remember, but the real account ID returned by AWS. Make this a mandatory step before high-risk commands:

# Safe habit before destructive commands
aws sts get-caller-identity --profile production
# Check the Account ID output matches expectations, then continue:
aws cloudformation delete-stack --stack-name legacy-app --profile production
Consider creating a simple shell alias like whoami-aws that runs aws sts get-caller-identity with the currently active profile. This small habit is far cheaper than the cost of recovering accidentally deleted production resources.

Using Profiles in Application Code

Profiles aren’t only used directly through AWS CLI — SDKs in various languages also support the same profile selection, reading from the exact same credentials and config files.

Node.js

const AWS = require('aws-sdk');

const credentials = new AWS.SharedIniFileCredentials({
  profile: 'staging',
});

AWS.config.credentials = credentials;

const s3 = new AWS.S3();

Go

package main

import (
	"context"
	"log"

	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"
)

func main() {
	cfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithSharedConfigProfile("staging"),
	)
	if err != nil {
		log.Fatal(err)
	}

	client := s3.NewFromConfig(cfg)
	_ = client
}

Python (boto3)

import boto3

session = boto3.Session(profile_name='staging')
s3 = session.client('s3')

The pattern in all three languages is the same: you don’t copy the Access Key and Secret Key directly into code, but refer to a profile name already configured in the credentials file. This aligns with the principle of avoiding hardcoded credentials — the SDK takes over reading the config file, and the application code only needs to know which profile name is used.

Don’t commit a production account profile name as a hardcoded default in application code shared across many environments. If the same code runs on another developer’s local development machine, they could accidentally access the production account because that profile exists on their computer with the same name.

Managing Profiles with Other Tools

Besides AWS CLI and application SDKs, infrastructure tools like Terraform also follow the same profile mechanism, because underneath they still read the standard ~/.aws/credentials and ~/.aws/config files.

provider "aws" {
  region  = "ap-southeast-1"
  profile = "staging"
}

Or via the same environment variable as AWS CLI:

export AWS_PROFILE=staging
terraform plan

For projects handling many environments at once, some teams use tools like direnv to automatically set the matching AWS_PROFILE when entering a particular project directory — so the active profile is always consistent with the project being worked on, without needing to remember to export it manually every time you switch terminals.

# .envrc in the staging project directory
export AWS_PROFILE=staging

Safe Practices to Prevent Using the Wrong Account

The profile mechanism itself doesn’t automatically prevent mistakes — it only provides the means. The work habits around it are what actually determine how safe a multi-account setup is in practice.

Use consistent, unambiguous profile names. Naming like dev, staging, prod is far safer than names relying on memory, like account1 or new-client-a. Naming consistency across team members also reduces miscommunication risk when coordinating.

Show the active profile in the shell prompt. Many shell configurations (zsh, bash with plugins) can display the AWS_PROFILE value directly in the terminal prompt, so you always see which profile is active without checking manually every time.

Verify before destructive commands, not after. As discussed in the previous section, aws sts get-caller-identity before commands that delete or modify resources should be a habit, not an optional step.

Avoid storing production credentials on the same machine as daily access. If possible, limit who has production credentials stored locally — the fewer computers storing them, the smaller the surface of leak or mistake risk.

flowchart TD
    A[Start a work session] --> B[Set AWS_PROFILE according to the project]
    B --> C[Check the shell prompt shows the active profile]
    C --> D{Destructive command?}
    D -- Yes --> E[Run sts get-caller-identity]
    E --> F{Account ID matches expectations?}
    F -- Yes --> G[Continue the command]
    F -- No --> H[STOP - switch the profile first]
    D -- No --> G

Summary Table

Aspectcredentials fileconfig file
ContentsAccess Key, Secret KeyRegion, output format
Data natureConfidentialNot confidential
Profile name format[name][profile name] (except default)
Default profile[default][default]
Activation MethodScopeWhen Used
--profile flagA single commandOccasionally switching accounts, not wanting to change env vars
AWS_PROFILE env varThe whole terminal sessionWorking consecutively on one same account
direnv per directoryAutomatically per projectMany projects with different accounts

Summary

  • AWS CLI stores credentials in ~/.aws/credentials and non-secret settings in ~/.aws/config — two files with different purposes.
  • Profile names in credentials are written plainly ([staging]), while in config they must be prefixed with profile ([profile staging]) except for default.
  • The default profile activates automatically if none is specified — never put production credentials there.
  • Activate a profile via the --profile flag for one-time use, or the AWS_PROFILE env var for the whole terminal session; the flag always wins when both are used together.
  • aws sts get-caller-identity is the most reliable way to verify which account is truly active — make it a mandatory habit before destructive commands.
  • SDKs in Node.js, Go, and Python all support the same profile selection, reading the same standard config files as AWS CLI.
  • Tools like Terraform follow the same profile mechanism; direnv can help automate profiles per project directory.
  • Consistent profile naming, an active profile visible in the shell prompt, and verification before risky commands are the habits that truly prevent mistakes — not just the technical mechanism alone.

Portfolio