iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🧢

Comparing AWS User Management Methods (January 2024)

に公開

Target Audience

  • People considering or wanting to improve AWS user management methods
  • People wanting to understand the functional differences of Identity Center depending on the presence of Organizations
  • People wanting IaC script samples for various methods
  • People wanting to know about the initial setup of Identity Center

Background

Recently, Identity Center, which previously required the use of Organizations as a prerequisite, has become available even without using Organizations.
https://aws.amazon.com/jp/about-aws/whats-new/2023/11/aws-iam-identity-center-account-instance-evaluation-adoption-managed-applications/

Since Identity Center can now be used for systems that do not require Organizations, the range of user management methods has expanded. I decided to compare and verify these methods to find the optimal approach.

Scripts Introduced in this Article

The scripts are available on GitHub, so please feel free to customize and use them.
https://github.com/Omanju23/zenn_aws_user-management

User Management Methods and Comparison Perspectives

This section describes the management methods to be verified in this article and the perspectives for comparison.

User Management Methods

Method Description
IAM User A method where IAM users belong to groups and perform various operations within the scope of the group's permissions.
IAM User + Role A method that grants the IAM group minimum permissions just to Switch Role and uses Assume Role to perform various operations.
This is the configuration I personally use most often.
Identity Center The method of using Identity Center without Organizations, which was recently added.
No IAM users are maintained.
Identity Center +
External IdP
The method of using Identity Center with Organizations.
No IAM users are maintained.

Comparison Perspectives

Perspective Description
Ease of Initial Setup Evaluates the difficulty of initial construction.
Operability Evaluates operability for adding/deleting users and daily usage.
Robustness (Security) Evaluates robustness against external attacks or information leakage.
Auditability Evaluates whether auditing is possible.
Other Extensibility Evaluates advantages unique to that method.

Comparison

In this article, we will verify each comparison perspective by performing the following common operations for each method:

  • Initial setup
  • Adding users and distributing login information to personnel
  • Operations using the CLI
  • Server login using Session Manager
  • Deleting users

Conclusion First

If you want to proceed with a one-off verification easily, just IAM users is recommended.
When operating a system with a single account, IAM User + Role.
If expansion (multi-account, multi-cloud, addition of service providers) is expected, Identity Center (with Organizations) would be a good choice.

Although I gave a "△" for the construction of Identity Center, personally I did not feel it was that difficult, so I would like to recommend Identity Center.
(Of course, this depends on the specific design of SSO and the increased design work associated with using Organizations, so it's hard to generalize...)

Method Ease of Initial
Setup
Operability Robustness Auditability Other Extensibility
IAM User ×
IAM User + Role ×
Identity Center
(Without Organizations)
- - - -
Identity Center
(With Organizations)

IAM User

Ease of Initial Setup

The following steps are performed to build the environment until the person in charge can log in.

  1. Create IAM users/groups with the root user.
  2. Create policies for the IAM group and assign permissions.
  3. Distribute the IAM user ID/PW to the person in charge.

The steps are very few, and it can be built in an instant once a CFn template is created.

This time, I used the following CFn template for construction.

iam template
---
---
AWSTemplateFormatVersion: "2010-09-09"
Description: A sample template
Resources:
  # Group for IAMUser
  IAMGroup:
    Type: AWS::IAM::Group
    Properties:
      GroupName: test-group

  IAMPolicy:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: test-policy
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "ec2:*"
              - "ssm:*"
              - "iam:ChangePassword"
            Resource: '*'
            Condition:
              BoolIfExists:
                aws:MultiFactorAuthPresent: 'true'
      Groups:
        - !Ref IAMGroup

  IAMPolicy2:
    Type: 'AWS::IAM::Policy'
    Properties:
      PolicyName: test-policy2
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "iam:ChangePassword"
            Resource: '*'
      Groups:
        - !Ref IAMGroup
  # IAMUser
  IAMUser:
    Type: AWS::IAM::User
    Properties:
      Groups: 
        - test-group
      LoginProfile: 
        Password: ************
        PasswordResetRequired: true
      UserName: test-user

Operability

  1. Adding users
    As mentioned before, adding is very easy.

  2. CLI operations and server connection via Session Manager
    I will configure port forwarding in the local environment using the AWS CLI and try to connect to the server.
    First, I will issue a long-term access key for using the AWS CLI in the local environment.

Next, set the issued access key/secret access key in .aws/credentials in the local home directory.

Configure port forwarding using the CLI and attempt to connect to the target using Teraterm.
I confirmed that port forwarding was successfully configured and the login prompt started.

start-session command
aws ssm start-session --target i-07d93d09436a5471d --document-name AWS-StartPortForwardingSession --parameters portNumber=22,localPortNumber=10022

  1. Deleting users
    If you delete the IAM user itself, the issued access keys will also be invalidated, so cleaning up is very easy.

Robustness

If the three elements—account ID, username, and PW—are leaked, malicious operations can be executed with the user's permissions.

Also, the hurdle for enforcing MFA devices is a bit high.
In the Assume Role method described later, you can simply enforce MFA devices with a Condition clause in the policy defining the Assume Role action. However, with only groups and users, you must add Condition clauses to the permitted actions themselves. For this reason, it is difficult to use AWS managed policies as they are, which is why I describe the hurdle for enforcing MFA devices as high.

Auditability

The API actions when the start-session command was issued in the "Operability" section are recorded in CloudTrail.
Since the username and action could be identified, it can be considered auditable.

Other Extensibility

None in particular.

IAM User + Role

Ease of Initial Setup

The following steps are performed to build the environment until the person in charge can log in.

  1. Create an IAM user/group with the root user.
  2. Create a policy for the IAM group and assign permissions.
    Here, specify the Role to switch to and enforce MFA devices.
  3. Create the Role that the user will switch to.
    Configure the trust policy so that it can be assumed by the IAM user in the target account.
    Since you don't need to add a Condition clause enforcing MFA to the policy attached to the Role, it's also possible to attach AWS managed policies.
  4. Distribute the IAM user ID/PW to the person in charge.

Compared to the IAM user-only method, one step has been added, but the difficulty can still be considered low.
In addition, because the enforcement of MFA devices is limited only to the assume-role action, the policies actually used for operation can be kept simple.

Operability

  1. Adding users
    As with the IAM user-only method, adding a user is very easy as you only need to add them to the IAM group.

  2. CLI operations and server connection via Session Manager
    Since a Role that can be assumed exists this time, it is possible to connect without issuing long-term access keys.
    Issue the following command to generate temporary credentials.

Issuing temporary credentials
aws sts assume-role --role-arn arn:aws:iam::447491875444:role/test2-role --role-session-name test
{
    "Credentials": {
        "AccessKeyId": "AS*************",
        "SecretAccessKey": "mSu**************************",
        "SessionToken": "IQoJb3JpZ2luX2VjEKb//////////wEaCXVzLWVhc3QtMSJIMEYCIQCdABtg8okGGUMOpZPsBieBGRdPeItF6198gJAGV2YhwwIhAKLnG7evEpYj3TPKPf+L1n9z0bmm1IepUdtT/1+Wa9R5KpUCCD4QABoMNDQ3NDkxODc1NDQ0Igz/B2n7NHaWIMvydLkq8gFOJaWqagap3PwmY+w7uElcLOUuark4vbCLDpHAlDLLWeSIdpjY7h8p0BfnRDkJosz+fZbvqL14uNoucwxHVrqbe4c/lVIq1KVuhQi/GxbQe6doq8rkSpTC0xtcLuNCh9Z4OfZ6WMG/i5XGlOGLxXqdjpfG0/ksNAJpOBxb927PY57WeWw9ZPygtX6WsL45uR1NkJ/4FWOQnpHer5ssoqy8vwP4VuA1decK13yDZJd4jX/aOyxJeBxnhm2Mhs+pbpVnh42a5uBG5k4uUg/wdHh7LLFtet+76neAG99ss69Eg2ptJmEf2+ZbuDvIlJIaAokHTTD6ss6sBjqcAXHNOGg1SBTEX54osLl1oTb1ci3yHOl5OFhIJz1UO0d1AehLYtZ7R4Usvy4eoI9yGm+L5qs6TV1v+C0qhCNFIDo+SS3WfEtMu03gQWQqFxGgWO1r91bAPkN5Ivr0wGCLWM0Lg8KbYUQ43vnDYrS0bPBpYbHPiE5Tbw4*************",
        "Expiration": "2024-01-02T06:04:58+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAWQMEYEJ2AOGSFBHCP:test",
        "A

Record the access key, secret access key, and session token obtained above in ~.aws/credentials.

Afterward, just as with an IAM user, configure port forwarding using the AWS CLI and connect.

  1. Deleting users
    As before, since you only need to delete the IAM user, cleaning up is very easy.

Robustness

In this configuration, if you need to perform actions using the AWS CLI, you can take two approaches.
One is the same as before: issuing long-term access keys.

The second is issuing temporary access keys using Assume Role.
This involves performing Assume Role in an environment like Cloud Shell and setting those credentials in a profile to execute processes.
Compared to the first method, this is more secure, and AWS also recommends issuing temporary credentials.

In this article, this configuration is evaluated as more robust than the IAM user-only method because it allows for obtaining temporary credentials.

Auditability

Although it adds one step compared to the user-only method, it can be audited via CloudTrail.
The left side of the image below shows the history of the AssumeRole action.
You can see that it was performed by test2-user.
The event on the right side is the history of the StartSession action.
The username is shown as "test," which corresponds to the session name specified during AssumeRole.

This can be confirmed from the event record of the AssumeRole action in CloudTrail.

As described above, although it involves the step of linking the action after AssumeRole with the session name at the time of AssumeRole, it can be said to be auditable.

Other Extensibility

None in particular.

Identity Center (Without Organizations)

Ease of Initial Setup

It is built in the following flow.

  1. Enabling Identity Center

  2. Configuring Identity Center (OTP when using Create User API, enforcing MFA devices)

  3. Creating groups

  4. Creating users

  5. Creating permission sets

  6. Enabling Identity Center
    First, enable Identity Center.
    Click "Enable" from the top page.

You can choose between with Organizations and without, but this time we select without.

Add tags as necessary and click "Enable" again.

After a while, the resource construction is completed, and the dashboard opens.

...Omitted (I'll leave the verification log here for now)...

  1. Creating permission sets
    I tried to create a permission set by calling the CreatePermissionSet API from the Identity Center API reference...

https://docs.aws.amazon.com/ja_jp/singlesignon/latest/APIReference/API_CreatePermissionSet.html

Creating a permission set using a Python script (Failed)
Cloudshell
[cloudshell-user@ip-10-132-39-203 ~]$ ls -l
total 4
-rw-r--r--. 1 cloudshell-user cloudshell-user 1193 Jan  6 08:28 create-permissionset.py
[cloudshell-user@ip-10-132-39-203 ~]$ 
[cloudshell-user@ip-10-132-39-203 ~]$ 
[cloudshell-user@ip-10-132-39-203 ~]$ cat create-permissionset.py 
import boto3
import json

def create_permission_set(instance_arm):
    client = boto3.client('sso-admin')
    response = client.create_permission_set(
        Description='test',
        InstanceArn=instance_arm,
        Name='group1-permission-set',
        Tags=[
            {
                'Key': 'Name',
                'Value': 'group1-permission-set'
            },
        ]
    )
    print(f"Permission Set {response['name']} created successfully")
    
    return response['PermissionSet']['PermissionSetArn']

def attach_managed_policy_to_permission_set(instance_arm,permission_set_arn):
    client = boto3.client('sso-admin')
    response = client.attach_managed_policy_to_permission_set(
        InstanceArn=instance_arm,
        ManagedPolicyArn='arn:aws:iam::aws:policy/PowerUserAccess',
        PermissionSetArn=permission_set_arn
    )
    print(f"Managed Policy {response['name']} attached successfully")
 
if __name__ == "__main__":
    instance_arm = "arn:aws:sso:::instance/ssoins-72231698d13b8cc7"
    permission_set_arn = create_permission_set(instance_arm)
    attach_managed_policy_to_permission_set(instance_arm,permission_set_arn)
[cloudshell-user@ip-10-132-39-203 ~]$ 
[cloudshell-user@ip-10-132-39-203 ~]$ 
[cloudshell-user@ip-10-132-39-203 ~]$ python create-permissionset.py 
Traceback (most recent call last):
  File "/home/cloudshell-user/create-permissionset.py", line 32, in <module>
    permission_set_arn = create_permission_set(instance_arm)
  File "/home/cloudshell-user/create-permissionset.py", line 6, in create_permission_set
    response = client.create_permission_set(
  File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 535, in _api_call
    return self._make_api_call(operation_name, kwargs)
  File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 980, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.errorfactory.ValidationException: An error occurred (ValidationException) when calling the CreatePermissionSet operation: The operation is not supported for this Identity Center instance

It turns out that permission sets cannot be created in account instances...

The operation is not supported for this Identity Center instance
To my surprise, it was not supported for this Identity Center instance without Organizations...
Indeed, reading the release article carefully, it only mentions support for managed applications...

This allows customers to quickly evaluate supported AWS managed applications, such as Amazon CodeCatalyst.

To make matters worse, the documentation also states that access to AWS accounts is not supported...

Account instances don't support permission sets and therefore don't support access to AWS accounts.

https://docs.aws.amazon.com/singlesignon/latest/userguide/account-instances-identity-center.html#supported-aws-applications

I am shocked to find out that the method that triggered this article could not be realized, but I will continue the verification.

Identity Center (With Organizations)

Ease of Initial Setup

The setup follows the flow below:

  1. Enabling Identity Center
  2. Configuring Identity Center (OTP when using Create User API, enforcing MFA devices)
  3. Creating groups
  4. Creating users
  5. Creating permission sets
  6. Account assignment

1. Enabling Identity Center

Just like with Identity Center (Without Organizations), launch an Identity Center instance.
This time, select "Enable with AWS Organizations".

It was created after a few minutes.

2. Identity Center Settings (OTP when using Create User API, Enforcing MFA devices)

Next, configure Identity Center.
Select "Settings" from the left pane.

First, enable OTP transmission when using the Create User API.
This is used when creating users via the CLI, SDK, etc. Details will be described later.


Next, set up the enforcement of MFA.
The default settings are as follows:
MFA device registration is forced at the first sign-in, and after that, it is only required if the sign-in context changes.

This time, I changed it to "Every sign-in" to align with the other methods.


3. Creating Groups

Since there are many parts of Identity Center and Identity Store that CloudFormation does not support, I will call various APIs using the SDK.
To create a group, use the Identity Store CreateGroup API.
https://docs.aws.amazon.com/singlesignon/latest/IdentityStoreAPIReference/API_CreateGroup.html

create-group.py
import boto3
import json

# Specify the Identity Store ID
identity_store_id = "d-********"

groups_data = [
    {"DisplayName": "Group1", "Description": "group1 description"},
    {"DisplayName": "Group2", "Description": "group2 description"},
    {"DisplayName": "Group3", "Description": "group3 description"}
]

# Add groups to AWS SSO
def create_group(group_data):
    client = boto3.client("identitystore")

    response = client.create_group(
      IdentityStoreId=identity_store_id,
      DisplayName=group_data["DisplayName"],
      Description=group_data["Description"]
    )

    print(f"Group {response['GroupId']} created successfully")



if __name__ == "__main__":
    
    for group_data in groups_data:
        create_group(group_data)

The Identity Store ID is listed in the identity source settings. It is the ID starting with d- shown in the bottom right of the image below.

I ran the above script on Cloud Shell and was able to create the groups successfully.

4. Creating Users

Next, I will create users to be assigned to the groups.
For this, I will also use the CreateUser API, which is an Identity Store API.
https://docs.aws.amazon.com/singlesignon/latest/IdentityStoreAPIReference/API_CreateUser.html

I created a script that handles both creating users and assigning them to groups.
With operations in mind, user-specific definitions are externalized in a JSON file.

create-user.py
import boto3
import json

# Specify the Identity Store ID
identity_store_id = "d-*******"

# Load user information
user_info_file = "user_info.json"

# Add user to AWS SSO
def create_user(user_data):
    client = boto3.client("identitystore")

    primary_email = user_data["emails"][0]

    response = client.create_user(
        IdentityStoreId=identity_store_id,
        UserName=user_data["user_name"],
        Name=user_data["name"],
        Emails=[
            {
                "Primary": primary_email["Primary"],
                "Type": primary_email["Type"],
                "Value": primary_email["Value"],
            }
        ],
        DisplayName=user_data.get("displayname", ""),  
        PreferredLanguage=user_data.get("language", "en-US"),
    )
    
    # Get the ID of the created user
    user_id = response["UserId"]  
    
    print(f"User {user_data['user_name']} created successfully")

    # Use ListGroups API to get group information
    groups_response = client.list_groups(IdentityStoreId=identity_store_id)
    group_id_mapping = {group["DisplayName"]: group["GroupId"] for group in groups_response.get("Groups", [])}

    # Linking to groups
    for group_name in user_data.get("groups", []):
        group_id = group_id_mapping.get(group_name)
        if group_id:
            response = client.create_group_membership(
                IdentityStoreId=identity_store_id,
                GroupId=group_id,
                MemberId={
                    'UserId': user_id
                }
            )
            print(f"User {user_data['user_name']} added to Group {group_name} successfully")
        else:
            print(f"Error: Group ID not found for Group {group_name}")



if __name__ == "__main__":
    with open(user_info_file, "r") as file:
        users_data = json.load(file)

    for user_data in users_data:
        create_user(user_data)

user_info.json
[
  {
    "user_name": "user1",
    "name": {
      "Formatted": "John Doe",
      "FamilyName": "Doe",
      "GivenName": "John"
    },
    "emails": [
      {
        "Primary": true,
        "Type": "Work",
        "Value": "john.doe@example.com"
      }
    ],
    "displayname": "John Doe",
    "language": "en-US",
    "groups": ["Group1","Group2"]
  },
  {
    "user_name": "user2",
    "name": {
      "Formatted": "Jane Smith",
      "FamilyName": "Smith",
      "GivenName": "Jane"
    },
    "emails": [
      {
        "Primary": true,
        "Type": "Work",
        "Value": "***************@gmail.com"
      }
    ],
    "displayname": "Jane Smith",
    "language": "ja-JP",
    "groups": ["Group1","Group3"]
  }
]

I also ran this on Cloud Shell and was able to create two users.

5. Creating Permission Sets

Next, I will create the permission sets to be given to the users created so far.
This uses Identity Center's CreatePermissionSet API and AttachManagedPolicytoPermissionSet API.
The former is used to create the permission set, and the latter is used to assign a managed policy to the created permission set.

create-permission-set.py
import boto3
import json

def create_permission_set(instance_arn):
    client = boto3.client('sso-admin')
    response = client.create_permission_set(
        Description='test',
        InstanceArn=instance_arn,
        Name='group1-permission-set',
        Tags=[
            {
                'Key': 'Name',
                'Value': 'group1-permission-set'
            },
        ]
    )
    print(f"Permission Set {response['PermissionSet']['Name']} created successfully")
    
    return response['PermissionSet']['PermissionSetArn']

def attach_managed_policy_to_permission_set(instance_arn,permission_set_arn):
    client = boto3.client('sso-admin')
    response = client.attach_managed_policy_to_permission_set(
        InstanceArn=instance_arn,
        ManagedPolicyArn='arn:aws:iam::aws:policy/PowerUserAccess',
        PermissionSetArn=permission_set_arn
    )
    print(f"Managed Policy attached successfully")
 
if __name__ == "__main__":
    instance_arn = "arn:aws:sso:::instance/************"
    permission_set_arn = create_permission_set(instance_arn)
    attach_managed_policy_to_permission_set(instance_arn,permission_set_arn)

I also ran this on Cloud Shell and created the permission set.

6. Account Assignment

Finally, I will assign the AWS account and principal to which the created permission set will be applied.
This uses Identity Center's CreateAccountAssignment API.

https://docs.aws.amazon.com/ja_jp/singlesignon/latest/APIReference/API_CreateAccountAssignment.html

create-account-assignment.py
import boto3
import json

def create_account_assignment():
    client = boto3.client('sso-admin')
    response = client.create_account_assignment(
      InstanceArn='arn:aws:sso:::instance/*****************',
      PermissionSetArn='arn:aws:sso:::permissionSet/*****************',
      PrincipalId='*****************',
      PrincipalType='GROUP',
      TargetId='4*************',
      TargetType='AWS_ACCOUNT'
    )
    print(f"AWS ACCOUNT attached successfully")
 
if __name__ == "__main__":
    create_account_assignment()

This was also successfully created.

With this, users belonging to Group 1 will be able to exercise the permissions (PowerUserAccess) included in the permission set (group1-permission-set) against the AWS account.

Operability

1. Creating Users

As mentioned in Identity Center (With Organizations) / 4. Creating Users, APIs are available, so creating users is very easy once you have a script.

2. CLI Operations and Server Connection via Session Manager

One of the features I consider to be an excellent point of Identity Center is this function.
Credentials can be obtained from the SSO portal as follows.

Additionally, you can create a profile interactively by using the aws sso configure command.

I succeeded in connecting via Session Manager based on the profile created above.

3. Deleting Users

As with other methods, you only need to delete the user in the Identity Store, so cleaning up is very easy.

Robustness

Next, I will evaluate robustness.
As mentioned in previous comparisons, it is easy to achieve robust operations because temporary credentials can be easily utilized.

Furthermore, unlike when using IAM users, there is no need to issue an initial password for the user provisioning flow.
(Depending on how much you care about it, but personally, I found this to be a nice mechanism.)

Below is the actual flow of user provisioning.

After creating a user, the SSO portal URL and username are provided to the user.
The user accesses the SSO portal.

When the user enters their user ID, a VerificationCode is sent to the email address registered at the time of creation.
→ This email is sent based on the settings made in 2. Identity Center Settings (OTP when using Create User API, Enforcing MFA devices).

After entering the VerificationCode, they set a new password.

Next, they proceed to the MFA device registration screen. Follow the on-screen instructions to register an MFA device.
This process is also implemented by the settings made in → 2. Identity Center Settings (OTP when using Create User API, Enforcing MFA devices).

Once the login process is complete, the portal is displayed.

As you can see, in the initial authentication process, there are few points where the administrator needs to intervene, and the process up to MFA device registration can be carried out smoothly.
In methods using Roles, users have to manually register MFA devices themselves after logging in, which often leads to inquiries. However, having a single direct path like this is convenient as users can register without confusion.

Also, compared to methods that do not use Identity Center, the SSO portal URL itself adds a layer of knowledge-based authentication, which results in a high evaluation.

Auditability

Finally, I will evaluate auditability.
The operability here is close to that of IAM users.
Since users in the Identity Store are displayed as the username in CloudTrail, it is very easy to understand.

Other Extensibility

Since Identity Center is an SSO service, it can be integrated with other SPs (Service Providers).
A list of supported services is provided in the following documentation for your reference.
https://docs.aws.amazon.com/ja_jp/singlesignon/latest/userguide/saasapps.html

Summary

The comparison table is as presented earlier.

Method Ease of Initial
Setup
Operability Robustness Auditability Other Extensibility
IAM User ×
IAM User + Role ×
Identity Center
(Without Organizations)
- - - -
Identity Center
(With Organizations)

Next, I would like to consider more optimal user management methods by verifying the design costs of Organizations and SSO integration between Identity Center and other service providers.

Discussion