Your team needs access to a second AWS account. The shortcut is usually to create another IAM user, generate an access key, and store the secret in ~/.aws/credentials—or in that vault whose name has become shorthand for “we’ll organize it later.” It works, but it leaves a long-term credential scattered around and multiplies the identities that must be revoked separately.
For people, the recommended path is federation with temporary credentials. With AWS IAM Identity Center, you assign a group to a Permission Set in each account. A person signs in once, chooses the account, and receives a limited session. By the end, we will verify two things: the active identity is an AWSReservedSSO_* role, and the old access key no longer works.
The example uses an organization instance of IAM Identity Center and AWS CLI v2. The account, instance, Permission Set, and group IDs are illustrative; do not run the administrative commands in production without review and change control.
What changes compared with an IAM user
An IAM user and its access key belong to one account. The key is a long-term credential: it remains valid until it is disabled or deleted. IAM Identity Center instead centralizes the human identity and issues temporary credentials for a specific combination of:
- group or user;
- Permission Set;
- target AWS account.
The IAM best practices documentation recommends federation and temporary credentials for human users. A Permission Set is the permissions model: it can combine AWS managed policies, customer managed policies, an inline policy, and, when needed, a permissions boundary.
A Permission Set session lasts one hour by default and can be configured for up to 12 hours. That is not the same as the access portal session, whose duration is configured separately. The credentials used by the CLI still expire even if the portal remains open.
The operational advantage becomes clear during revocation. Instead of searching for AKIA... keys in notebooks, environment variables, and old profiles, you remove the group assignment from the account. To observe the change safely, check Identity Center and sign-in events in CloudTrail and confirm the assignments in the portal. The audit trail can answer “which group received which Permission Set in which account” instead of “who might still be using this key?”
This applies to human access. CI workloads follow a different path: OIDC, roles, and their own policies. If that is your case, see GitHub Actions with AWS OIDC. Human SSO and workload identity solve adjacent problems; they are not interchangeable.
A Permission Set provisions the role—you do not create a competing one
There is no operational choice between manually creating an IAM role and creating a Permission Set for the same flow. For multi-account access, Identity Center uses the Permission Set as a template and provisions a service-controlled role in every assigned account:
AWSReservedSSO_<permission-set-name>_<unique-suffix>
Outside us-east-1, the ARN includes the region of the Identity Center instance:
arn:aws:iam::<account>:role/aws-reserved/sso.amazonaws.com/<region>/AWSReservedSSO_<name>_<suffix>
When the instance is in us-east-1, the region segment is omitted. You do not edit this role's trust policy to add IAM users, services, or other principals. Identity Center owns the role, and only identities assigned through it enter by this path.
ABAC is a separate configuration as well. User attributes can become session tags and participate in Permission Set or resource policies, but aws:PrincipalTag and aws:RequestTag are not the default trust mechanism for the reserved role.
The complete flow is:
- An organization instance enables multi-account access in the AWS organization.
- An administrator creates the Permission Set.
- A group receives that Permission Set in a target account.
- Identity Center creates and maintains the
AWSReservedSSO_*role in the account. - The person chooses the account and role through the portal or CLI and receives temporary credentials.
An account instance does not replace this prerequisite: it cannot grant access to AWS accounts. To verify provisioning without changing anything, open IAM in the target account and find the role under /aws-reserved/sso.amazonaws.com/. Also check the assignment in the Identity Center console.
There is one important trap: when you remove every assignment for a Permission Set in an account, the reserved role is deleted. If it is created again, its suffix changes. Resources such as a KMS key policy or older EKS configurations that pin the complete ARN will stop recognizing the role. When an integration must reference it, follow the AWS-documented ArnLike pattern and preserve at least one administrative assignment during the transition.
Defining the Permission Set and assignment as code
CloudFormation provides the AWS::SSO::PermissionSet and AWS::SSO::Assignment resources. This excerpt creates a read-only Permission Set with an eight-hour session and assigns it to a group. Again, the identifiers are examples.
Resources:
PermissionSet:
Type: AWS::SSO::PermissionSet
Properties:
InstanceArn: arn:aws:sso:::instance/ssoins-instanceId
Name: WorkloadDev
SessionDuration: PT8H
ManagedPolicies:
- arn:aws:iam::aws:policy/ReadOnlyAccess
Assignment:
Type: AWS::SSO::Assignment
Properties:
InstanceArn: arn:aws:sso:::instance/ssoins-instanceId
PermissionSetArn: !GetAtt PermissionSet.PermissionSetArn
TargetId: "123456789011"
TargetType: AWS_ACCOUNT
PrincipalType: GROUP
PrincipalId: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
The official references for Permission Set and Assignment describe the accepted properties. Prefer groups over individual assignments: people join and leave through the identity provider, while account access remains versioned in the template.
If you are validating the flow with the administrative CLI, the equivalent commands start like this:
aws sso-admin create-permission-set \
--instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx \
--name WorkloadDev \
--session-duration PT8H
aws sso-admin attach-managed-policy-to-permission-set \
--instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx \
--permission-set-arn arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxxx/ps-xxxxxxxxxxxxxxxx \
--managed-policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess
aws sso-admin create-account-assignment \
--instance-arn arn:aws:sso:::instance/ssoins-xxxxxxxxxxxxxxxx \
--target-id 123456789011 \
--target-type AWS_ACCOUNT \
--permission-set-arn arn:aws:sso:::permissionSet/ssoins-xxxxxxxxxxxxxxxx/ps-xxxxxxxxxxxxxxxx \
--principal-type GROUP \
--principal-id aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
Creating the assignment is asynchronous. Check describe-account-assignment-creation-status with the returned request ID before testing access; repeatedly signing in while provisioning is still running merely turns propagation into a criminal investigation. Later changes to Permission Set policies must be provisioned again to assigned accounts with provision-permission-set.
In production, observe these events in CloudTrail, retain the template and the review that approved the change, and avoid parallel edits in the console. When assigning access to the organization's management account, also review the additional administrative permissions required; member accounts do not have exactly the same requirement.
Configuring an SSO profile in AWS CLI v2
Use the format with an sso-session section, which supports token renewal, rather than the legacy SSO profile. The following configuration belongs in ~/.aws/config:
[profile workload-dev]
sso_session = company-sso
sso_account_id = 123456789011
sso_role_name = WorkloadDev
region = us-east-1
[sso-session company-sso]
sso_region = us-east-1
sso_start_url = https://my-sso-portal.awsapps.com/start
sso_registration_scopes = sso:account:access
The sso_role_name value is the name shown in the portal—that is, the Permission Set name. The account must be the target account, not the management account chosen out of convenience.
You can generate this configuration interactively:
aws configure sso
AWS CLI v2 has used PKCE by default since version 2.22.0. When the browser is on another device, use aws configure sso --use-device-code. Then sign in and verify the identity:
aws sso login --profile workload-dev
aws sts get-caller-identity --profile workload-dev
GetCallerIdentity requires no specific IAM permission. An expected response has this shape:
{
"UserId": "AROAxxxxxxxx:session-name",
"Account": "123456789011",
"Arn": "arn:aws:sts::123456789011:assumed-role/AWSReservedSSO_WorkloadDev_<suffix>/<session-name>"
}
Three signals matter:
Accountis the target account;Arncontainsassumed-role/AWSReservedSSO_;UserIdcontains the role identifier followed by the session name and does not begin like an IAM user withAIDA....
Under the hood, the CLI authenticates through OIDC in the Identity Center portal, obtains the token, and requests temporary credentials for that account and Permission Set. The person does not use a static access key to run sts assume-role manually.
If the result still shows arn:aws:iam::...:user/..., stop before declaring the migration complete. Run:
aws configure list --profile workload-dev
The credential source should not appear as shared-credentials-file pointing to an access key. The AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN variables can also take precedence over the expected profile. Remove them from the shell, then repeat the login and get-caller-identity:
unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN
aws sso login --profile workload-dev
aws sts get-caller-identity --profile workload-dev
The SSO cache lives in ~/.aws/sso/cache; it stores temporary material and does not turn the session into another permanent key. To end the local sessions stored by the CLI:
aws sso logout
Proving that the old key is out of the path
A successful SSO response proves that the new path works, but it does not prove that the old one is dead. Retire it in a controlled sequence:
- Confirm with the owner that no workload depends on the human key.
- Disable the legacy IAM user's access key.
- Test the SSO profile and confirm the
AWSReservedSSO_*ARN. - Separately test a profile containing only the disabled key.
- After the agreed observation window and rollback plan, delete the key and, if nothing else depends on it, the IAM user.
Do not mix the two profiles during the test. Otherwise, the credential chain may find a static key, and you will end up validating the past with impressive confidence.
For a deleted key, an S3 call through the CLI should fail with InvalidAccessKeyId:
AWS_PROFILE=legacy-human aws s3 ls
An STS call may return UnrecognizedClientException:
AWS_PROFILE=legacy-human aws sts get-caller-identity
InvalidClientTokenId is a generic invalid-token message and should not be used as specific proof that an access key was deleted. ExpiredTokenException, in turn, indicates an expired temporary session, not the removal of an IAM key.
The observation checklist is compact:
| Check | Expected result | Fix if it fails |
|---|---|---|
aws configure list --profile workload-dev | No access key sourced from shared-credentials-file or the environment | Remove the static key and variables from the process |
aws sts get-caller-identity --profile workload-dev | Correct account and an assumed-role/AWSReservedSSO_* ARN | Review sso_account_id, sso_role_name, and the assignment |
| Identity Center portal | Group, account, and Permission Set are visible | Correct the assignment and wait for its status to complete |
| CloudTrail | Sign-in and administrative events match the change | Review the region, event source, and queried time window |
| Legacy profile after deletion | InvalidAccessKeyId in S3 or UnrecognizedClientException in STS | Verify that the test did not pick up another credential in the chain |
When to adopt this design
For people accessing one or more accounts in an AWS organization, use an organization instance of IAM Identity Center, group assignments, and version-controlled Permission Sets. Keep long-term access keys only as documented exceptions for tools that genuinely cannot use temporary credentials—and track each exception as debt with an owner and a deadline.
The next step is small: choose one group and one non-production account, create a read-only Permission Set, sign in with AWS CLI v2, and save the output from get-caller-identity as migration evidence. Only then disable the old key during a controlled window and confirm in CloudTrail that the federated path remains in use.