Your Local AWS Setup: Stop Hardcoding Access Keys Forever
Table of Contents
You grep your machine for AKIA and find three matches: one in .bashrc, one in a .env file you forgot about, and one in your shell history that is also synced to iCloud. You rotate the key. A week later, your agent creates a new project and drops a fresh access key into ~/.aws/credentials because that is what the AWS docs say to do.
Time: About 30 minutes.
Outcome: AWS CLI v2 configured with IAM Identity Center (SSO), named profiles for dev/staging/prod, session credentials that expire automatically, and aws-vault for an extra layer of protection. Zero long-lived access keys on your machine.
What We’re Building
- AWS CLI v2 with IAM Identity Center (SSO) authentication
- Named profiles for dev, staging, and production environments
- aws-vault for encrypted credential storage
- Automatic session expiration (no permanent keys)
- Shell integration with profile switching
Why This Matters
Open your terminal and run this:
grep -r "AKIA" ~/.aws ~/.bashrc ~/.zshrc ~/.bash_profile 2>/dev/null
If you see output, you have AWS access keys sitting in plain text on your machine. Now imagine what’s in your team’s Slack history, .env files, and git commits.
The access key lifecycle of doom:
┌─────────────────────────────────────────────────────────────────┐
│ THE ACCESS KEY LIFECYCLE OF DOOM │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Day 1: "I'll just hardcode this key for now" │
│ │ │
│ ▼ │
│ Day 7: Key ends up in .env file │
│ │ │
│ ▼ │
│ Day 30: .env gets committed to git │
│ │ │
│ ▼ │
│ Day 31: Bot scrapes GitHub, mines crypto on your account │
│ │ │
│ ▼ │
│ Day 32: $47,000 AWS bill │
│ │
└─────────────────────────────────────────────────────────────────┘
This isn’t hypothetical. GitHub’s secret scanning blocks thousands of AWS keys daily, but only catches what gets pushed to public repos. Private repos, Slack messages, local files? You’re on your own.
What we’re building today:
- AWS CLI v2 configured properly
- IAM Identity Center (SSO) for short-term, auto-rotating credentials
- Named profiles for managing multiple accounts/environments
- aws-vault as a fallback for accounts without SSO
- A workflow where credentials never touch plain text files
By the end, you’ll have a setup that’s both more secure and more convenient than hardcoded keys.
Installing the AWS CLI v2
The AWS CLI is your primary interface to AWS outside the console. Version 2 is required because it has SSO support that v1 lacks.
Option 1: Homebrew (Recommended)
brew install awscliOption 2: Official Installer
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
rm AWSCLIV2.pkg curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
rm -rf awscliv2.zip aws/For ARM64 (Graviton, Apple Silicon VMs):
curl "https://awscli.amazonaws.com/awscli-exe-linux-aarch64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
rm -rf awscliv2.zip aws/ Option 1: MSI Installer
Download and run: AWS CLI MSI Installer
Option 2: winget
winget install Amazon.AWSCLI Verify Installation
aws --version
aws-cli/2.15.0 Python/3.11.6 Darwin/23.0.0 source/arm64 The version should start with 2.. If you see 1.x, you have the old CLI. Uninstall it and install v2.
Why CLI v2? Version 2 includes built-in SSO support (aws sso login), credential caching, and automatic token refresh. These features don’t exist in v1. If you’re on v1, upgrade now.
Authentication Methods Compared
Before diving into setup, understand your options:
┌─────────────────────────────────────────────────────────────────────────────┐
│ AWS AUTHENTICATION METHODS │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ METHOD CREDENTIALS ROTATION BEST FOR │
│ ────── ─────────── ──────── ──────── │
│ │
│ IAM Identity Short-term Automatic Teams with │
│ Center (SSO) (hours) (seamless) AWS Organizations │
│ ✅ RECOMMENDED │
│ │
│ ──────────────────────────────────────────────────────────────────────────│
│ │
│ aws-vault + Short-term Per-command Solo accounts, │
│ Access Keys (via STS) (manual) legacy setups │
│ ⚠️ FALLBACK │
│ │
│ ──────────────────────────────────────────────────────────────────────────│
│ │
│ Plain Access Long-term Manual CI/CD only │
│ Keys in files (forever) (never done) (if no OIDC) │
│ ❌ AVOID │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
| Method | Security | Convenience | Setup Complexity | Team Size |
|---|---|---|---|---|
| IAM Identity Center | Highest | Highest | Medium | Any |
| aws-vault + Access Keys | High | Medium | Low | Solo / Small |
| Plain Access Keys | Low | Low | Lowest | Never for humans |
The recommendation is clear: use IAM Identity Center if you can. It provides short-term credentials that rotate automatically, integrates with your browser for authentication, and scales from solo developers to enterprise teams.
If you’re on a solo AWS account without Organizations, aws-vault is your next best option. It wraps access keys with encryption and STS to provide similar security benefits.
IAM Identity Center: The Modern Way
IAM Identity Center (formerly AWS SSO) is AWS’s solution for human access to AWS accounts. Instead of long-lived access keys, you authenticate through your browser and receive short-term credentials that auto-refresh.
┌─────────────────────────────────────────────────────────────────────────────┐
│ IAM IDENTITY CENTER FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ aws sso login ┌──────────────────┐ │
│ │ │ ────────────────────▶ │ │ │
│ │ Terminal │ │ Browser Opens │ │
│ │ │ │ (SSO Portal) │ │
│ └──────────┘ └────────┬─────────┘ │
│ │ │
│ │ You authenticate │
│ │ (password + MFA) │
│ ▼ │
│ ┌──────────────────┐ │
│ │ │ │
│ │ Identity Center │ │
│ │ validates you │ │
│ │ │ │
│ └────────┬─────────┘ │
│ │ │
│ │ Issues temporary │
│ │ credentials │
│ ▼ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ │ ◀──────────────────── │ │ │
│ │ Terminal │ Credentials cached │ ~/.aws/sso/cache │ │
│ │ │ (auto-refresh) │ │ │
│ └──────────┘ └──────────────────┘ │
│ │
│ Credentials expire in 1-12 hours (configurable). │
│ CLI auto-refreshes them when needed. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Prerequisites
IAM Identity Center requires:
- AWS Organizations enabled (even for a single account)
- Identity Center enabled in your management account
If you set up your account following Part 1 of this series, you still need to enable Organizations. Part 1 does not cover that step. Follow the guide step below to enable it before proceeding.
Clean up the Part 1 IAM user. Now that SSO handles authentication, deactivate (or delete) the IAM user you created in Part 1. Having unused credentials is a security risk. In the IAM console, disable the access keys and remove console access for that user.
Configure the AWS CLI for SSO
Now configure your local CLI to use Identity Center:
aws configure sso
The CLI will prompt you for:
SSO session name (Recommended): my-sso
SSO start URL [None]: https://d-xxxxxxxxxx.awsapps.com/start
SSO region [None]: us-east-1
SSO registration scopes [sso:account:access]: Find your SSO start URL: In the Identity Center console, look at the Settings page. The “AWS access portal URL” is your start URL.
After entering these, your browser opens for authentication. Once authenticated, the CLI shows your available accounts and permission sets:
There are 2 AWS accounts available to you.
> my-company-prod, account-id@email.com (123456789012)
my-company-dev, dev@email.com (987654321098) Select an account and permission set, then:
CLI default client Region [None]: us-east-1
CLI default output format [None]: json
CLI profile name [AdministratorAccess-123456789012]: prod-admin Give it a memorable profile name. prod-admin is better than AdministratorAccess-123456789012.
Your Config File
After setup, your ~/.aws/config looks like this:
[sso-session my-sso]
sso_start_url = https://d-xxxxxxxxxx.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access
[profile prod-admin]
sso_session = my-sso
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
output = json
Notice: No credentials in the config file. The credentials are obtained dynamically when you run aws sso login.
Daily Workflow
First command of the day:
aws sso login --profile prod-admin
Your browser opens, you authenticate (with MFA), and you’re done. Credentials are cached and auto-refresh for the session duration.
Use the profile:
# Explicit profile
aws s3 ls --profile prod-admin
# Or set the environment variable
export AWS_PROFILE=prod-admin
aws s3 ls
Verify your identity:
aws sts get-caller-identity --profile prod-admin
{
"UserId": "AROAXXXXXXXXXXXXXXXXX:your.email@company.com",
"Account": "123456789012",
"Arn": "arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_AdministratorAccess_abc123/your.email@company.com"
} Multiple Accounts
Add more profiles for other accounts or permission sets:
aws configure sso --profile dev-admin
Or manually add to ~/.aws/config:
[profile dev-admin]
sso_session = my-sso
sso_account_id = 987654321098
sso_role_name = AdministratorAccess
region = us-east-1
output = json
[profile prod-readonly]
sso_session = my-sso
sso_account_id = 123456789012
sso_role_name = ViewOnlyAccess
region = us-east-1
output = json
One aws sso login authenticates you to the SSO session. All profiles sharing that session work immediately.
| ❌ Under | One set of access keys in ~/.aws/credentials. Same keys for dev and production. Keys never rotated. The agent uses whatever credentials it finds, which happen to have admin access to production. |
| ✅ Right | IAM Identity Center with named profiles. Session credentials that expire in 1-12 hours. aws-vault for encrypted storage. Separate profiles per environment so --profile prod is a deliberate choice. |
| ❌ Over | Hardware security keys required for every CLI session. Custom credential broker with audit logging for a solo developer. Separate laptops for production access. |
| 🤖 Agent Trap | Agent runs aws configure and stores a permanent access key in plaintext because it is the fastest path to a working CLI. The key has admin permissions “for development.” Six months later, nobody remembers the key exists, it has never been rotated, and it has full access to production. |
Named Profiles: Managing Multiple Identities
Whether you use SSO or access keys, profiles let you manage multiple AWS identities without confusion.
The Two Config Files
AWS CLI uses two files:
~/.aws/ config # Profiles, regions, output format, SSO settings credentials # Access keys (only if using long-term credentials)
With SSO, you only need config. The credentials file is for legacy access key storage.
Profile Structure
# ~/.aws/config
# Default profile (used when no --profile specified)
[default]
region = us-east-1
output = json
# SSO-based profile
[profile prod-admin]
sso_session = my-sso
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
# Role assumption profile (covered later)
[profile deploy-role]
source_profile = prod-admin
role_arn = arn:aws:iam::123456789012:role/DeployRole
region = us-east-1
# Legacy access key profile (avoid for humans)
[profile legacy-service]
# credentials stored in ~/.aws/credentials
region = us-east-1
Using Profiles
Method 1: Flag
aws s3 ls --profile prod-admin
Method 2: Environment variable
export AWS_PROFILE=prod-admin
aws s3 ls # Uses prod-admin
Method 3: Default profile
If [default] is configured, commands without --profile use it. But explicit is better than implicit, so use AWS_PROFILE or --profile.
Profile Naming Conventions
Use a consistent naming scheme:
{environment}-{permission-level}
Examples:
prod-admin: Production account, admin accessprod-readonly: Production account, read-onlydev-admin: Development account, admin accessstaging-deploy: Staging account, deployment permissions
This makes it obvious what you’re targeting before you run a command.
Legacy Setup: Access Keys + aws-vault
If you’re on a solo AWS account without Organizations, or working with a legacy setup that doesn’t have Identity Center, aws-vault provides similar security benefits.
Why aws-vault?
Plain access keys in ~/.aws/credentials are:
- Stored in plain text
- Never rotated automatically
- Easily leaked via backups, screen shares, or dotfile repos
aws-vault:
- Stores keys in your OS keychain (encrypted)
- Uses STS to issue short-term credentials
- Never exposes long-term keys to your shell
┌─────────────────────────────────────────────────────────────────────────────┐
│ aws-vault FLOW │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ │
│ │ Long-term keys │ Stored encrypted in OS keychain │
│ │ (Access Key ID + │ (macOS Keychain, Windows Credential Manager, │
│ │ Secret Key) │ Linux secret-service) │
│ └────────┬─────────┘ │
│ │ │
│ │ aws-vault exec profile -- command │
│ ▼ │
│ ┌──────────────────┐ │
│ │ │ │
│ │ aws-vault │──────▶ Calls STS GetSessionToken │
│ │ │ │
│ └────────┬─────────┘ │
│ │ │
│ │ Receives temporary credentials (1 hour) │
│ ▼ │
│ ┌──────────────────┐ │
│ │ │ │
│ │ Your command │ Runs with short-term credentials │
│ │ runs │ in environment variables │
│ │ │ │
│ └──────────────────┘ │
│ │
│ Long-term keys NEVER appear in environment or plain files. │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Installing aws-vault
brew install aws-vault # Download the latest release
sudo curl -L -o /usr/local/bin/aws-vault \
https://github.com/99designs/aws-vault/releases/latest/download/aws-vault-linux-amd64
sudo chmod 755 /usr/local/bin/aws-vaultNote: On Linux, aws-vault needs a secrets backend. Install pass or gnome-keyring:
# For pass (recommended)
sudo apt install pass
gpg --gen-key
pass init "your-gpg-key-id"
export AWS_VAULT_BACKEND=pass # Using Chocolatey
choco install aws-vault
# Or Scoop
scoop install aws-vault Adding Credentials
First, create access keys for your IAM user (from Part 1):
Now add them to aws-vault (NOT to ~/.aws/credentials):
aws-vault add prod-admin
Enter Access Key ID: AKIAIOSFODNN7EXAMPLE
Enter Secret Access Key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Added credentials to profile "prod-admin" in vault The keys are now encrypted in your OS keychain. Verify they’re not in plain text:
cat ~/.aws/credentials # Should be empty or not exist
Using aws-vault
Run a single command:
aws-vault exec prod-admin -- aws s3 ls
Open a subshell with credentials:
aws-vault exec prod-admin
# Now in a subshell with temporary credentials
aws s3 ls
aws ec2 describe-instances
exit # Credentials gone
Check what’s in your vault:
aws-vault list
Profile Credentials Sessions
======= =========== ========
prod-admin prod-admin sts.GetSessionToken:12h0m0s
dev-admin dev-admin - Configuring Profiles for aws-vault
Even with aws-vault, you still use ~/.aws/config for profile settings:
# ~/.aws/config
[profile prod-admin]
region = us-east-1
output = json
mfa_serial = arn:aws:iam::123456789012:mfa/your-username
[profile dev-admin]
region = us-east-1
output = json
With mfa_serial set, aws-vault prompts for your MFA code when getting credentials:
aws-vault exec prod-admin -- aws s3 ls
Enter MFA code for arn:aws:iam::123456789012:mfa/your-username: 123456
MFA + aws-vault is powerful. Every command requires MFA, and credentials last only 1 hour by default. Even if someone accesses your machine, they can’t use your AWS access without your MFA device.
Assuming Roles Locally
Sometimes you need to assume a different role for deployment, cross-account access, or elevated permissions. Both SSO and aws-vault support this.
Role Assumption with SSO Profiles
If your Identity Center permission set maps to a role, you’re already assuming that role. But for additional role chaining:
# ~/.aws/config
[profile prod-admin]
sso_session = my-sso
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
[profile prod-deploy]
source_profile = prod-admin
role_arn = arn:aws:iam::123456789012:role/DeployRole
region = us-east-1
When you use prod-deploy, the CLI:
- Gets credentials from
prod-admin(via SSO) - Calls
sts:AssumeRoleonDeployRole - Returns temporary credentials for
DeployRole
aws sts get-caller-identity --profile prod-deploy
Role Assumption with aws-vault
# ~/.aws/config
[profile prod-admin]
region = us-east-1
mfa_serial = arn:aws:iam::123456789012:mfa/your-username
[profile prod-deploy]
source_profile = prod-admin
role_arn = arn:aws:iam::123456789012:role/DeployRole
mfa_serial = arn:aws:iam::123456789012:mfa/your-username
aws-vault exec prod-deploy -- aws sts get-caller-identity
aws-vault handles the MFA prompt and role assumption automatically.
Cross-Account Role Assumption
For accessing resources in another AWS account:
[profile other-account-admin]
source_profile = prod-admin
role_arn = arn:aws:iam::999888777666:role/CrossAccountAdmin
region = us-east-1
This assumes:
- You have a role
CrossAccountAdminin account999888777666 - That role’s trust policy allows your identity to assume it
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "sts:AssumeRole"
}
]
}
The Credential Chain: How AWS Finds Your Credentials
When you run an AWS command, the SDK looks for credentials in this order:
┌─────────────────────────────────────────────────────────────────────────────┐
│ AWS CREDENTIAL RESOLUTION ORDER │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Environment Variables │
│ AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN │
│ │ │
│ ▼ (if not found) │
│ 2. Shared Credentials File │
│ ~/.aws/credentials [profile] or [default] │
│ │ │
│ ▼ (if not found) │
│ 3. Shared Config File │
│ ~/.aws/config (for SSO, role assumption) │
│ │ │
│ ▼ (if not found) │
│ 4. Container Credentials │
│ ECS task role, EKS pod identity │
│ │ │
│ ▼ (if not found) │
│ 5. Instance Profile │
│ EC2 instance metadata service (IMDS) │
│ │ │
│ ▼ (if not found) │
│ 6. Error: Unable to locate credentials │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Why This Matters
Environment variables override everything. If you have AWS_ACCESS_KEY_ID set from an old session, it wins over your carefully configured SSO profile.
Debug credential issues with:
# What profile is active?
echo $AWS_PROFILE
# Any credentials in environment?
env | grep AWS
# Who does the CLI think you are?
aws sts get-caller-identity
Common Pitfalls
| Problem | Symptom | Fix |
|---|---|---|
| Stale env vars | Wrong account/user | unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN |
| Wrong profile | ”Access denied” for resources you own | Check AWS_PROFILE or use --profile |
| SSO session expired | ”Token has expired” | Run aws sso login --profile xxx |
| Mixed auth methods | Unpredictable behavior | Pick one method, clean up the others |
Clean up old credentials. If you previously used access keys, remove them from ~/.aws/credentials and environment variables after switching to SSO or aws-vault. Mixed authentication causes confusion.
Quick Wins
Shell Aliases
Add to your ~/.bashrc or ~/.zshrc:
# Quick profile switching
alias awsp='export AWS_PROFILE=$(aws configure list-profiles | fzf)'
# Show current identity
alias awswho='aws sts get-caller-identity'
# SSO login shortcut
alias awslogin='aws sso login --profile'
# aws-vault shortcuts (if using)
alias ave='aws-vault exec'
alias avl='aws-vault list'
With fzf installed, awsp gives you an interactive profile picker.
Auto-Completion
# Add to ~/.bashrc
complete -C '/usr/local/bin/aws_completer' aws # Add to ~/.zshrc
autoload bashcompinit && bashcompinit
autoload -Uz compinit && compinit
complete -C '/usr/local/bin/aws_completer' aws Now aws s3<tab> shows available subcommands.
Output Formats
Configure default output format per profile:
[profile prod-admin]
output = json # Default: verbose, parseable
# output = table # Human-readable tables
# output = text # Tab-separated, good for scripting
# output = yaml # YAML format
Or override per command:
aws ec2 describe-instances --output table
Useful CLI Options
# Dry run (see what would happen)
aws ec2 run-instances --dry-run ...
# Query specific fields with JMESPath
aws ec2 describe-instances --query 'Reservations[*].Instances[*].InstanceId' --output text
# Filter results
aws ec2 describe-instances --filters "Name=instance-state-name,Values=running"
# Pagination for large results
aws s3api list-objects-v2 --bucket my-bucket --max-items 100
Validation Checklist
Verify your local AWS setup is complete:
AWS CLI
AUTHENTICATION
PROFILES
CLEANUP
0 of 0 completed
Verification Commands
Run these to confirm your setup:
# CLI version
aws --version
# Current identity
aws sts get-caller-identity --profile your-profile
# Check for leaked credentials
grep -r "AKIA" ~ --include="*.env" --include=".*rc" --include=".*profile" 2>/dev/null
# List your profiles
aws configure list-profiles
Key Takeaways
- IAM Identity Center replaces long-lived access keys with session credentials that expire automatically, which eliminates the entire category of “leaked permanent key” incidents.
- Named profiles force you to choose an environment explicitly, which means deploying to production requires typing
--profile prodinstead of accidentally using your default credentials. - aws-vault encrypts credentials at rest so a stolen laptop does not mean stolen AWS access.
- The
AKIAgrep test should return zero results on your machine. If it does not, you have permanent credentials that need to be replaced with SSO sessions.
What’s Coming
Next in Part 4: Terraform Fundamentals, we codify everything in infrastructure-as-code. The SSO profiles you configured today become the authentication method Terraform uses to manage your AWS resources.
Have questions? Found an error? Open an issue or reach out on LinkedIn.
Related Posts
Your First 60 Minutes in AWS: What Everyone Skips and Regrets Later
What everyone skips and regrets later. Set up your AWS account the right way: secure enough to sleep at night, without the enterprise overhead that kills velocity.
IAM: The Key to Everything
Least privilege without losing your mind. Build a working IAM setup for your startup, not enterprise complexity, not 'just use AdministratorAccess.' The practical middle ground.
Terraform Fundamentals: Why Clicking Is Technical Debt
Stop clicking through the AWS console. Set up your Terraform project from scratch with remote state, proper structure, and templates for everything we've built so far.
OpenTelemetry Setup: Observability That Grows With You
Set up the OTel Collector once, choose your backend. SigNoz or Grafana stack. Your observability foundation for the entire series.
Git & GitHub: The Foundation Your Whole Team Builds On
The foundation your whole team, humans and agents, builds on. Git conventions, agent commit hygiene, and attribution that tracks who wrote what.
Branch Protection & PR Workflow: Quality Gates for Humans and Agents
Quality gates that catch both human and agent mistakes. Branch protection, PR templates with agent-review sections, and CODEOWNERS for your growing team.
Pre-Commit Hooks & Code Quality: Your Agents' First Guardrail
Your agents' first guardrail, and your Scorecard's first data source. Pre-commit hooks that catch what human review misses, especially from AI-generated code.