Still using IAM Users for AWS access? You're not alone. According to Datadog's 2024 State of Cloud Security report, 46% of organizations still rely on IAM Users for AWS access. However, with AWS Identity Center (formerly AWS SSO), there's a better way to manage access that improves security and reduces administrative overhead.
In a previous piece, we covered the planning process involved to move from using AWS IAM Users, to AWS IAM Identity Center. In this piece, we’ll show the exact AWS CLI commands needed to perform the migration steps.
Moving from IAM Users to Identity Center offers several key benefits:
- Improved security through temporary credentials instead of long-lived access keys
- Centralized identity management across multiple AWS accounts
- Simplified user lifecycle management with IdP integration
- Consistent security controls through centrally managed policies
- Easier access auditing and reporting
The AWS Command Line Interface
The AWS CLI is a unified tool to manage your AWS services. To get started:
- Download and install from the official AWS CLI page
- Configure with
aws configure
or set up SSO withaws configure sso
- Verify your installation with
aws --version
For detailed installation instructions for your operating system, visit the AWS CLI User Guide.
In these examples, the variables are given in $VARIABLE
format, so that you can replace it with your own IDs and values.
Planning the Move
Timeline
Defining your timeline is a planning activity, not a technical one. It depends on your priorities, resources, and existing infrastructure, more than any specific CLI commands.
External Dependencies
If you don't have an identity provider (IdP) already available to store your identities, then now is the time to set one up. The process for choosing and setting up an IdP is more than technical, and will depend upon your choice. When working with IDC the available IdP options are:
- Identity Center directory
- Active Directory
- External identity provider
Set-up Identity Center
With your planning and external dependencies in place, you can start configuring your AWS-based resources.
It is possible to create an IDC instance via the CLI, but not recommended. As detailed in the documentation, the command will fail if you try to create it in the organization management account, or if there is already an instance of IDC in the account. Since you should be using an organization instance of IDC, this unfortunately means you will need to use the AWS console to create your instance.
Once you have an IDC instance available, you can check its configuration with the list-instances
command, because there will only ever be one instance in an AWS account, across all regions:
# Get the information for your IDC instance, and take note of InstanceArn
# for future commands
aws sso-admin list-instances
All IDC administration commands are under the sso-admin
command namespace. The sso
and identitystore
command groups are used for logging in to IDC, and managing users if IDC is also your IdP, respectively.
Configure Your IdP
If your identity provider is newly set up, ensure it’s configured to work with IDC. The official AWS documentation includes step-by-step tutorials for the most popular enterprise IdPs in the market:
Define Your Policies
To have a successful migration, you need to first take stock of what you’re using in your environment.
Audit existing IAM users
Start by listing all your users in your environment, so you know exactly what you need to get rid of:
# List all IAM users
aws iam list-users
AWS IAM has a nice built-in feature to give you a summary report of all the long-term credentials being used in your environment:
# Generate and retrieve credential report
aws iam generate-credential-report
# Get the report, no ID needed since there can be only one report at a time
aws iam get-credential-report
The AWS documentation includes detailed explanation of the report fields, but at a high level it includes
- User information, such as when they were created
- Authentication method details, such as password age and if MFA is enabled
- Access key information, such as their status, and when and where they were last used
- If X.509 signing certificates have been active
For the access key usage, this information can be pulled directly from the API for a specific key, without needing to wait for a report to be generated with the command:
# View activity for a single access key
aws iam get-access-key-last-used --access-key-id $ACCESS_KEY_ID
If you use IAM groups, then they are a good starting point for roles to consider. Review your groups and the policies attached to them, so that you have a good starting point for the permissions your shared roles should have:
# List groups of users
aws iam list-groups
# Get all users in specific group
aws iam get-group --group-name $GROUP_NAME
# List inline policies
aws iam list-group-policies --group-name $GROUP_NAME
# List AWS and customer managed policies
aws iam list-attached-group-policies --group-name $GROUP_NAME
Review attached policies to find specific permissions that should be included in your new, role-based policies
# List AWS and customer managed policies
aws iam list-attached-user-policies --user-name $USERNAME
# List inline policies
aws iam list-user-policies --user-name $USERNAME
Use IAM Access Analyzer
AWS IAM Access Analyzer has many features, but the most relevant to your migration is the policy generation ability:
# Generate policy recommendations based on usage, returns a jobId
aws accessanalyzer start-policy-generation \
--policy-generation-details \
principalArn=arn:aws:iam::$ACCOUNT_ID:user/$USERNAME \
--cloud-trail-details \
'{
"accessRole": "arn:aws:iam::$ACCOUNT_ID:role/service-role/AccessAnalyzerMonitorServiceRole",
"startTime": "2024-11-22T00:30:00Z",
"trails": [
{
"allRegions": true,
"cloudTrailArn": "arn:aws:cloudtrail:$REGION:$ACCOUNT_ID:trail/$TRAIL_NAME"
}
]
}'
# Get the generated policy for the returned jobId
aws accessanalyzer get-generated-policy --job-id $JOB_ID
There is no charge for the policy generation functionality of Access Analyzer.
The JSON-based arguments can get unwieldy, so you can also put the contents in a JSON file, and use the file-based argument --cloud-trail-details file://$FILENAME.json
instead of inline JSON as a string. The example above shows a user’s history being used to generate a policy, but Access Analyzer works with IAM roles too.
Create Permissions Sets
At this stage of migration, you should have a good idea of the policies that you need to be able to assign to your Identity Center-based users and groups. To assign them, the permissions need to be defined as IDC permissions sets
# Create a permission set, noting the returned PermissionSetArn
aws sso-admin create-permission-set \
--instance-arn $INSTANCE_ARN \
--name "ReadOnlyAccess" \
--description "Provides read-only access"
# Attach the ReadOnlyAccess managed policy to permission set
aws sso-admin attach-managed-policy-to-permission-set \
--instance-arn $INSTANCE_ARN \
--permission-set-arn $PERMISSION_SET_ARN \
--managed-policy-arn arn:aws:iam::aws:policy/ReadOnlyAccess
aws sso-admin attach-customer-managed-policy-reference-to-permission-set \
--instance-arn $INSTANCE_ARN \
--permission-set-arn $PERMISSION_SET_ARN \
--customer-managed-policy-reference \
'{
"Name": "MyCustomPolicy",
"Path": "/"
}'
# List permission sets for review
aws sso-admin list-permission-sets \
--instance-arn $INSTANCE_ARN
This example shows a permission set with the AWS managed policy ReadOnlyAccess
being attached. Having a role that allows viewing, but not changing, your environment is one of the most common RBAC roles. AWS managed policies are useful because they are created by the services teams to encapsulate the permissions required to use the service with different levels of access. You can see the full list of AWS managed policies in the official documentation.
Assign Permissions Sets
To be used, permissions sets must be assigned to users or groups in your IdP, which are identified in IDC by a GUID:
# Create account assignment
aws sso-admin create-account-assignment \
--instance-arn $INSTANCE_ARN \
--permission-set-arn $PERMISSION_SET_ARN \
--principal-id $PRINCIPAL_GUID \
--principal-type USER \
--target-id $ACCOUNT_ID \
--target-type AWS_ACCOUNT
# List assignments for review
aws sso-admin list-account-assignments \
--account-id $ACCOUNT_ID \
--instance-arn $INSTANCE_ARN \
--permission-set-arn $PERMISSION_SET_ARN
In the create-account-assignment
command example, the values “USER
” and “AWS_ACCOUNT
” are literal string values required for the command to work, and not placeholders for your own variables.
Safe, Staged Migration
By now, users logging in to your AWS environment should have access to start using the new IDC-based access on a regular basis. Even with careful planning, and services like Access Analyzer, it’s all too easy to miss permissions that are actually required.
Check for Permissions Errors
The AWS auditing service, CloudTrail, has a built-in event history functionality that you can use to look up specific user activity. This can help you ensure they have access to the right permissions, and address any permissions that might’ve been missed in the planning phase.
The CloudTrail event history only lasts for 90 days maximum, and it’s not possible to search for AccessDenied
errors directly. For longer history, you will need to use an S3 or CloudWatch Logs-based trail, which you can then use Athena or another analytics service to find the most interesting events in your CloudTrail. If your CloudTrail logs to CloudWatch Logs, you can use this command to search for AccessDenied
errors:
aws logs filter-log-events \
--region $REGION \
--start-time $EPOCH_TIME_MS \
--log-group-name CloudTrail/DefaultLogGroup \
--filter-pattern AccessDenied
Where $EPOCH_TIME_MS
is the number of milliseconds since January 1, 1970 UTC.
Disable old Access
Disabling access is critical to actually benefiting from the migration process, but disable the wrong access, and you could impact running services, and cause systems outages. When IAM evaluates requests, any deny statements override allow statements. This means you can add a deny all policy to a user, and don’t have to remove existing policies to test what removing their access will do in your environment:
# Apply a deny all policy to IAM user
aws iam put-user-policy \
--user-name $USERNAME \
--policy-name DenyAll \
--policy-document \
'{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "*",
"Resource": "*"
}
]
}'
Setting an access key to “Inactive” before you delete it means that if it disabling it causes issues, you can re-enable it by setting the status to “Active” and restore access quickly.
# Deactivate an access key
aws iam update-access-key \
--access-key-id $ACCESS_KEY_ID \
--status Inactive \
--user-name $USERNAME
Clean-up for Security
Congratulations! By this stage you and your teams are using IDC for AWS access on a daily basis. You’ve completed the hardest part of the migration, but there’s still more work to be done to realize the full value of migrating off users: Cleaning up unused resources. By removing old resources, you reduce the risk of them being used or accidentally exposed.
Delete old Access
# Remove a user's login profile, so they can't log in with a password
aws iam delete-login-profile --user-name $USERNAME
# Delete access keys, so they can't access AWS programmatically
aws iam delete-access-key --access-key-id $ACCESS_KEY_ID --user-name $USERNAME
# Remove a user from a group
aws iam remove-user-from-group --user-name $USERNAME --group-name $GROUP_NAME
# Finally, delete the user
aws iam delete-user --user-name $USERNAME
Review Access
Now that you’ve cleaned up old, unused access, all that’s left to do is to refine your access over time. There’s no fixed or “right” schedule for this, and the frequency you review will depend on the rate of change in your environment, your risk appetite, and compliance posture.
Some of the areas you should be focusing on include:
- Permissions access requests from users
- Just-in-time access provisioning
- Unused permissions
- On-boarding and off-boarding process
- Access denied errors in CloudTrail
At the end of this migration, you'll have transformed your AWS access management from a potential security liability into a modern, secure foundation. Your users will have seamless access across accounts, your security team will have better visibility and control, and you'll have eliminated the risks of long-term credentials.