Exploring a Better AWS CLI Authentication Workflow
Written by
At
noidilin
Mon May 25 2026

AWS authentication from the terminal is one of those topics that feels simple until it becomes part of daily work.
At first, I only wanted a clean way to run aws commands locally. Then Terraform entered the picture. Then multiple AWS accounts entered the picture. Then multiple projects and clients entered the picture. At that point, the question stopped being "how do I log in?" and became something closer to:
What does a sane AWS CLI workflow look like when the work has to scale beyond one machine, one account, and one person's preferences?
I already had a pattern that worked for me. But I also had this uncomfortable feeling that "works for me" is not the same as "good practice." I still have to work with a team. I cannot always force everyone to install the same helper tool or think about credentials the same way. And even if I could, that does not automatically mean my pattern would survive at a larger scale.
So this post is a learning log about AWS CLI authentication, AWS IAM Identity Center / SSO, Terraform, and the breakpoints I started to notice while comparing tools like aws-sso-cli and granted.
What I wanted to understand
The concrete questions were:
- How should I authenticate from the terminal without long-lived IAM user access keys?
- How should Terraform get AWS credentials locally?
- What changes when I have many AWS accounts, projects, or clients?
- Is my personal CLI setup something a team can depend on?
- Where does the workflow start to break?
What I learned
My current mental model is:
- Authentication should be centralized. Prefer AWS IAM Identity Center / SSO or another federated identity flow over static IAM user credentials.
- Authorization should be account/project specific. Use roles and permission sets that describe the work being done, not one broad credential for everything.
- The AWS CLI profile is the contract. Helper tools can improve the login experience, but most tools still converge on
AWS_PROFILE,~/.aws/config, and the AWS SDK credential chain. - Terraform should not need a special auth universe. Locally, it can use the same profile-based credentials as the AWS CLI. In CI, it should use workload identity / OIDC or another non-human credential flow.
- A personal tool is not automatically a team standard. The team standard should be the credential model and profile naming convention. The helper tool can be a recommendation, not always a hard requirement.
The durable primitive is not aws-sso-cli or granted. The durable primitive is:
identity provider -> AWS role/session -> named profile -> AWS SDK credential chain -> toolOnce I saw that chain, the tool comparison became less emotional. A helper tool is good when it makes that chain safer, clearer, or less annoying. It is dangerous when it hides the chain so well that nobody knows which account or role they are using.
The baseline: avoid long-lived IAM user keys
The old AWS CLI setup many people learn first looks like this:
aws configureThen they paste:
AWS Access Key ID
AWS Secret Access Key
Default region
Default output formatThis works, but it creates a long-lived credential on a developer machine. That is the part I want to avoid as a default practice.
Long-lived access keys have a few problems:
- they can be copied out of
~/.aws/credentials - they may live longer than the project itself
- rotation is easy to forget
- they do not naturally express "I am doing this task for this account right now"
- when leaked, they can be used until revoked
For a personal toy account, maybe this is tolerable. For client work or team infrastructure, it feels like the wrong default.
The better baseline is temporary credentials from a federated login flow. In AWS terms, that usually means IAM Identity Center / SSO for humans, or role assumption from another identity provider.
With SSO-style profiles, the AWS CLI config looks more like a description of where to get temporary credentials:
[profile client-a-dev-admin]
sso_start_url = https://example.awsapps.com/start
sso_region = ap-southeast-1
sso_account_id = 111122223333
sso_role_name = AdministratorAccess
region = ap-southeast-1
output = jsonThe important difference is that the profile does not contain a permanent secret. It tells the CLI how to obtain a session.
Profiles are the real interface
The most useful thing I learned is that AWS profiles are the common interface between many tools.
The AWS CLI uses profiles:
AWS_PROFILE=client-a-dev-admin aws sts get-caller-identityTerraform can also use the same credential chain:
AWS_PROFILE=client-a-dev-admin terraform planOr the provider can name the profile explicitly:
provider "aws" {
region = "ap-southeast-1"
profile = "client-a-dev-admin"
}I usually prefer the environment variable for local work because it keeps Terraform configuration less tied to my personal profile names. The repo can document the expected account and role, while my local machine maps that to the actual profile.
That gives me a practical boundary:
- repository code should know the target account shape and required permissions
- my machine should know how to log in as me
AWS_PROFILEconnects those worlds for local commands
This is also where mistakes happen. If the profile name is vague, like admin or default, then every command carries hidden risk. terraform apply with the wrong profile is not a small typo.
So profile naming matters more than I expected.
A useful naming pattern is something like:
<client-or-org>-<environment>-<role>For example:
acme-dev-readonly
acme-dev-admin
acme-prod-readonly
acme-prod-breakglassThe name should make accidental context switches obvious.
Where helper tools fit
Tools like aws-sso-cli and granted sit around the AWS CLI credential flow. They do not replace IAM, permission sets, roles, or Terraform's provider authentication. They make the human workflow easier.
That distinction matters.
A helper tool can provide:
- nicer profile discovery
- easier SSO login
- clearer account/role selection
- shell integration
- credential export for tools that need environment variables
- visual reminders about the active account
But the underlying model is still AWS temporary credentials.
This makes me less interested in asking "which tool is the best?" in an absolute way. The better question is:
Which tool makes the correct workflow easiest for this team?
aws-sso-cli
My current approach uses aws-sso-cli. I like it because it makes SSO-based work more pleasant and gives me a clearer interface around accounts and roles than raw aws sso login commands.
It fits well when I already know that my world is AWS SSO / IAM Identity Center and I want better ergonomics around that.
The risk is team adoption. If a project README says "run this custom helper command" before explaining the AWS profile and role model, the tool becomes a magic incantation. New teammates may be able to run the command without understanding what identity they have assumed.
That is convenient, but also fragile.
A useful comparison point is aws-vault. I think of aws-vault as starting from the problem of safely storing and brokering AWS credentials, especially static access keys and role-assumption workflows, while aws-sso-cli starts from the assumption that IAM Identity Center is already the center of gravity. The aws-sso-cli comparison makes this distinction clear: aws-vault fits well when the important problem is securing credentials around the existing ~/.aws/config model; aws-sso-cli fits better when the problem is discovering, selecting, and naming many SSO accounts and roles without manually writing one profile block per role. That makes the choice less like "which tool is more secure?" and more like "which source of complexity am I actually trying to reduce?"
granted
granted feels more focused on making role assumption and multi-account switching pleasant, especially when someone works across many accounts. The idea of a clearer "assume this role" workflow is attractive for consultant-style work, where account switching is constant.
It may be a better fit when the main pain is not just logging in, but repeatedly choosing the correct account and role without losing track of context.
Again, the same warning applies: the tool should make the AWS model visible, not hide it completely.
Terraform should use the same local identity story
Terraform often makes authentication feel like a separate topic, but for local development I think it should be boring.
If I can run:
AWS_PROFILE=acme-dev-admin aws s3 lsthen I should usually be able to run:
AWS_PROFILE=acme-dev-admin terraform planThat does not mean every developer should have admin access. It means Terraform should consume credentials through the standard AWS provider chain instead of asking everyone to invent a second credential setup.
The more important Terraform-specific practice is separating human local access from automation access.
For local work:
human login -> SSO session -> AWS_PROFILE -> terraform plan/applyFor CI/CD:
CI identity -> OIDC / trusted role -> temporary AWS credentials -> terraform plan/applyCI should not depend on someone's SSO session. It should not use copied access keys from a developer machine. It should have its own trust path.
That separation makes the local workflow comfortable without turning it into production automation.
The multiple-client problem
The part that made me think hardest was not the syntax. It was the consulting-style scenario: many projects, many accounts, many clients, and different expectations in every team.
My personal machine might have profiles like:
client-a-dev-admin
client-a-prod-readonly
client-b-staging-admin
client-c-security-audit
personal-lab-adminThis is manageable for me, but it creates questions:
- Does every client use IAM Identity Center?
- Are profile names standardized across the team?
- Do permission sets mean the same thing in each organization?
- Can I document my workflow without forcing my exact tooling on everyone?
- How do I avoid accidentally running commands against the wrong client?
This is where "best practice" becomes less clean.
At the individual level, I want a fast workflow. At the team level, I want a shared convention. At the client level, I may have to respect an existing identity setup that I did not design.
The best answer I have right now is to standardize the smallest useful surface:
- Use temporary credentials, not long-lived IAM user keys.
- Use named profiles with explicit account/environment/role names.
- Document the required profile contract per repository.
- Let people use helper tools if they produce the same profile/session behavior.
- Use separate CI authentication that does not depend on human sessions.
That gives room for personal tooling without making the whole team depend on my preferences.
Potential breakpoints
The workflow starts to break when the hidden assumptions become bigger than the documented contract.
1. Profile names are personal and undocumented
If Terraform examples say:
AWS_PROFILE=admin terraform applythat is not enough. Admin of what? Which account? Which environment? Which client?
A better README should say something like:
This repository expects credentials for:
Account: acme-dev / 111122223333
Role: PowerUserAccess or project-specific Terraform role
Region: ap-southeast-1
Suggested local profile name: acme-dev-terraformThe exact profile name can be local, but the account and role expectation must be explicit.
2. The helper tool becomes the standard instead of the auth model
If the team says "install this tool" but never explains IAM Identity Center, profiles, roles, or temporary credentials, the practice becomes shallow.
That is fine until something breaks. Then nobody knows whether the problem is:
- expired SSO session
- wrong AWS profile
- wrong role
- missing permission set
- wrong region
- Terraform backend credentials
- provider credentials
A good helper tool should reduce friction, not remove understanding.
3. Terraform state and provider auth get mixed up
Terraform can need AWS credentials for more than one thing:
- reading/writing remote state, such as S3 backend access
- acquiring state locks, such as DynamoDB locking in older setups
- managing resources through the AWS provider
Those can be the same role, but they do not have to be. In larger setups, backend access and deployment permissions may be intentionally separated.
If I treat "Terraform auth" as one blob, I may miss that distinction.
4. Production access is too convenient
A great CLI workflow can become dangerous if production access feels the same as development access.
For production, friction can be a feature:
- readonly by default
- separate production profiles
- shorter sessions
- stronger approval path
- explicit break-glass roles
- shell prompt warnings
The goal is not to make production impossible to work with. The goal is to make context visible before a command changes something expensive.
5. Local practice leaks into CI
A human SSO workflow is for humans. CI needs a different pattern.
If a pipeline depends on static access keys because that was the quickest way to make Terraform run, the team has recreated the long-lived credential problem in a more dangerous place.
For modern CI, I would rather use OIDC into an AWS role where possible. The pipeline receives temporary credentials for that run, scoped to that repository/environment relationship.
My current preferred workflow
For now, my preferred shape is:
- Use AWS IAM Identity Center / SSO for human access.
- Keep AWS profiles explicit and boring.
- Use a helper tool like
aws-sso-cliorgrantedfor ergonomics. - Run AWS CLI and Terraform through
AWS_PROFILE. - Document the expected account, region, and role in each repo.
- Keep CI authentication separate from local authentication.
For example:
export AWS_PROFILE=acme-dev-terraform
aws sts get-caller-identity
terraform planThe sts get-caller-identity step looks small, but I like it as a ritual. It answers the most important question before I do anything else:
Who am I, according to AWS, right now?That question is more reliable than my memory.
What still feels unresolved
The uncomfortable part is that this is not only a technical design. It is also a team agreement.
I can choose a careful setup for myself. I can write clean profile names. I can prefer temporary credentials. But real projects involve other people, existing client constraints, half-documented history, and deadlines that make shortcuts tempting.
That uncertainty is tiring. It makes "best practice" feel less like a fixed answer and more like a moving boundary between safety, convenience, and what the team can actually maintain.
But I also still enjoy this kind of exploration. There is something satisfying about taking a cloudy operational habit and turning it into smaller pieces: identity, role, session, profile, tool, provider, pipeline. Once the pieces have names, the workflow becomes less magical.
My current answer is not "everyone should use exactly my setup." It is:
Standardize the credential model, document the profile contract, and let helper tools compete on ergonomics.
That feels like a better team practice than arguing over one perfect CLI wrapper.
The next thing I want to understand is how to design the repository-level documentation for this: enough structure that a teammate can safely run Terraform, but not so much that every project becomes a custom onboarding ceremony.
Don't use Shadcn UI!
a personal opinion for shadcn ui, and it was...
My Attempt to Sandbox an AI Agent with IAM Identity Center
A learning log about using IAM Identity Center, permission sets, permission boundaries, and short-lived AWS CLI sessions to make agent-assisted DevOps feel less like a black box.