-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Marketplace Contribution] AWS - IAM Identity Center #28559
[Marketplace Contribution] AWS - IAM Identity Center #28559
Conversation
Thank you for your contribution. Your generosity and caring are unrivaled! Rest assured - our content wizard @anas-yousef will very shortly look over your proposed changes. |
@@ -190,4 +191,4 @@ def get_timeout(timeout): | |||
raise DemistoException("You can specify just the read timeout (for example 60) or also the connect " | |||
"timeout followed after a comma (for example 60,10). If a connect timeout is not " | |||
"specified, a default of 10 second will be used.") | |||
return read_timeout, connect_timeout | |||
return read_timeout, connect_timeout |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was this change made by mistake?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't change that pack maybe something happened while submitting my pack
], | ||
DisplayName=f'{userDisplayName}' | ||
) | ||
print(response) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to remove the print function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py
Outdated
Show resolved
Hide resolved
remove_user_from_groups(args, client) | ||
|
||
except Exception as e: | ||
LOG(str(e)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return_error returns the LOG object with the error, no need to call it here
LOG(str(e)) |
if __name__ in ('__builtin__', 'builtins', '__main__'): | ||
main() | ||
|
||
register_module_line('AWS - IAM Identity Cetner', 'end', __line__()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
register_module_line('AWS - IAM Identity Cetner', 'end', __line__()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't forget to delete this file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a README file for the integration. You can use the documentation found here to do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Hey @sepaprivate, |
…AWSIAMIdentityCenter.py Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
Fixing couple of issues
Hi @anas-yousef any update on this pack? |
Hello @sepaprivate, I saw that you added new changes. I am currently going over them |
#def update_user(args, client): # pragma: no cover | ||
# kwargs = {'UserName': args.get('oldUserName')} | ||
# if args.get('newUserName'): | ||
# kwargs.update({'NewUserName': args.get('newUserName')}) | ||
# if args.get('newPath'): | ||
# kwargs.update({'NewPath': args.get('newPath')}) | ||
# | ||
# response = client.update_user(**kwargs) | ||
# if response['ResponseMetadata']['HTTPStatusCode'] == 200: | ||
# demisto.results( | ||
# "Changed UserName {0} To: {1}".format(args.get('oldUserName'), args.get('newUserName'))) | ||
|
||
|
||
#def delete_user(args, client): # pragma: no cover | ||
# response = client.delete_user(UserName=args.get('userName')) | ||
# if response['ResponseMetadata']['HTTPStatusCode'] == 200: | ||
# demisto.results('The User {0} has been deleted'.format(args.get('userName'))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these needed? If not, please delete them
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|
||
if role_policy is not None: | ||
kwargs.update({'Policy': role_policy}) | ||
elif self.aws_role_policy is not None: | ||
kwargs.update({'Policy': self.aws_role_policy}) | ||
|
||
demisto.debug('{kwargs}='.format(kwargs=kwargs)) | ||
|
||
if kwargs and not self.aws_access_key_id: # login with Role ARN | ||
if not self.aws_access_key_id: | ||
sts_client = boto3.client('sts', config=self.config, verify=self.verify_certificate, | ||
region_name=region if region else self.aws_default_region, | ||
endpoint_url=self.sts_endpoint_url) | ||
sts_response = sts_client.assume_role(**kwargs) | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=sts_response['Credentials']['AccessKeyId'], | ||
aws_secret_access_key=sts_response['Credentials']['SecretAccessKey'], | ||
aws_session_token=sts_response['Credentials']['SessionToken'], | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
elif self.aws_access_key_id and (role_arn or self.aws_role_arn): # login with Access Key ID and Role ARN | ||
sts_client = boto3.client( | ||
service_name='sts', | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.sts_endpoint_url | ||
) | ||
kwargs.update({ | ||
'RoleArn': role_arn or self.aws_role_arn, | ||
'RoleSessionName': role_session_name or self.aws_role_session_name, | ||
}) | ||
sts_response = sts_client.assume_role(**kwargs) | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=sts_response['Credentials']['AccessKeyId'], | ||
aws_secret_access_key=sts_response['Credentials']['SecretAccessKey'], | ||
aws_session_token=sts_response['Credentials']['SessionToken'], | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
elif self.aws_session_token and not self.aws_role_arn: # login with session token | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
aws_session_token=self.aws_session_token, | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
elif self.aws_access_key_id and not self.aws_role_arn: # login with access key id | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
else: # login with default permissions, permissions pulled from the ec2 metadata | ||
client = boto3.client(service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
endpoint_url=self.endpoint_url) | ||
|
||
return client | ||
|
||
@staticmethod | ||
def get_timeout(timeout): | ||
if not timeout: | ||
timeout = "60,10" # default values | ||
try: | ||
|
||
if isinstance(timeout, int): | ||
read_timeout = timeout | ||
connect_timeout = 10 | ||
|
||
else: | ||
timeout_vals = timeout.split(',') | ||
read_timeout = int(timeout_vals[0]) | ||
# the default connect timeout is 10 | ||
connect_timeout = 10 if len(timeout_vals) == 1 else int(timeout_vals[1]) | ||
|
||
except ValueError: | ||
raise DemistoException("You can specify just the read timeout (for example 60) or also the connect " | ||
"timeout followed after a comma (for example 60,10). If a connect timeout is not " | ||
"specified, a default of 10 second will be used.") | ||
return read_timeout, connect_timeout | ||
|
||
register_module_line('AWSApiModule', 'end', __line__(), wrapper=1) | ||
### END GENERATED CODE ### |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this was added by accident? I'll remove it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it is reauired
from CommonServerPython import * # noqa: F401 | ||
|
||
|
||
import botocore.exceptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes botocore is essential for AWS authentication and other methods
# def update_user(args, client): # pragma: no cover | ||
# kwargs = {'UserName': args.get('oldUserName')} | ||
# if args.get('newUserName'): | ||
# kwargs.update({'NewUserName': args.get('newUserName')}) | ||
# if args.get('newPath'): | ||
# kwargs.update({'NewPath': args.get('newPath')}) | ||
# | ||
# response = client.update_user(**kwargs) | ||
# if response['ResponseMetadata']['HTTPStatusCode'] == 200: | ||
# demisto.results( | ||
# "Changed UserName {0} To: {1}".format(args.get('oldUserName'), args.get('newUserName'))) | ||
|
||
|
||
# def delete_user(args, client): # pragma: no cover | ||
# response = client.delete_user(UserName=args.get('userName')) | ||
# if response['ResponseMetadata']['HTTPStatusCode'] == 200: | ||
# demisto.results('The User {0} has been deleted'.format(args.get('userName'))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this needed? If not, please delete it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
- name: familyName | ||
- name: givenName | ||
required: true | ||
- name: userEmailAddress | ||
required: true | ||
- name: displayName | ||
required: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add descriptions for these arguments.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
description: Adds the specified user to the specified group. | ||
name: aws-iam-identitycenter-add-user-to-group | ||
- arguments: | ||
- name: groupName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a description here for this argument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
description: This will remove the entered user from all groups/memberships. | ||
name: aws-iam-identitycenter-remove-user-from-all-groups | ||
- arguments: | ||
- name: emailAddress |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add a description here for this argument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once you update the yml file, I will update the README to reflect the latest changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anas-yousef all required changes are done, for your review please!
Remove commented functions
Adding Required Descriptions to arguments
…tyCenter' into sepaprivate-contrib-AWS-IAMIdentityCenter
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add the description of the outputs that I mentioned, and let us talk over Slack so we can set up time for a demo :)
- contextPath: AWS.IAM.IdentityCenter.Users.UserName | ||
type: string | ||
- contextPath: AWS.IAM.IdentityCenter.Users.Email | ||
type: string | ||
- contextPath: AWS.IAM.IdentityCenter.Users.UserId | ||
type: string | ||
- contextPath: AWS.IAM.IdentityCenter.Users.DisplayName | ||
type: string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add descriptions for these outputs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @sepaprivate,
I reviewed your demo and everything looks good. I added some small comments. Take a look at them whenever you can.
Address the errors in the yml file.
elif command == 'aws-iam-identitycenter-get-group': | ||
get_user(args, client) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the correct method for the command aws-iam-identitycenter-get-group?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch thank you, I fixed it
'Email': user['Emails'][0]['Value'], | ||
'DisplayName': user['DisplayName'] | ||
} | ||
userID = user['UserId'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason you only take the last UserId, since multiple users can be returned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, there will be always one user returned not multiple. I'm fetching list of users from AWS then looping on that list to check if the queried user is available or not. So, always one userId will be returned so i can use it in listing group assigned to that user later on.
Fix issues
Modified couple of functions, and removed unnecessary function (Date) added the AmazonAPIModule as the import didn't work
Fix some formatting
Add missing description fields
Fixing missing fields and updating tag for boto3py3 docker image
@anas-yousef all issues have been fixed. Thank you |
Post Fixes
Changes in Parameter Configurations
Hi @anas-yousef, the integration is now fully updated. Please for your review. |
…tyCenter' into sepaprivate-contrib-AWS-IAMIdentityCenter
Updating Docker image tag to latest version
|
||
if role_policy is not None: | ||
kwargs.update({'Policy': role_policy}) | ||
elif self.aws_role_policy is not None: | ||
kwargs.update({'Policy': self.aws_role_policy}) | ||
|
||
demisto.debug('{kwargs}='.format(kwargs=kwargs)) | ||
|
||
if kwargs and not self.aws_access_key_id: # login with Role ARN | ||
if not self.aws_access_key_id: | ||
sts_client = boto3.client('sts', config=self.config, verify=self.verify_certificate, | ||
region_name=region if region else self.aws_default_region, | ||
endpoint_url=self.sts_endpoint_url) | ||
sts_response = sts_client.assume_role(**kwargs) | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=sts_response['Credentials']['AccessKeyId'], | ||
aws_secret_access_key=sts_response['Credentials']['SecretAccessKey'], | ||
aws_session_token=sts_response['Credentials']['SessionToken'], | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
elif self.aws_access_key_id and (role_arn or self.aws_role_arn): # login with Access Key ID and Role ARN | ||
sts_client = boto3.client( | ||
service_name='sts', | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.sts_endpoint_url | ||
) | ||
kwargs.update({ | ||
'RoleArn': role_arn or self.aws_role_arn, | ||
'RoleSessionName': role_session_name or self.aws_role_session_name, | ||
}) | ||
sts_response = sts_client.assume_role(**kwargs) | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=sts_response['Credentials']['AccessKeyId'], | ||
aws_secret_access_key=sts_response['Credentials']['SecretAccessKey'], | ||
aws_session_token=sts_response['Credentials']['SessionToken'], | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
elif self.aws_session_token and not self.aws_role_arn: # login with session token | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
aws_session_token=self.aws_session_token, | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
elif self.aws_access_key_id and not self.aws_role_arn: # login with access key id | ||
client = boto3.client( | ||
service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
aws_access_key_id=self.aws_access_key_id, | ||
aws_secret_access_key=self.aws_secret_access_key, | ||
verify=self.verify_certificate, | ||
config=self.config, | ||
endpoint_url=self.endpoint_url | ||
) | ||
else: # login with default permissions, permissions pulled from the ec2 metadata | ||
client = boto3.client(service_name=service, | ||
region_name=region if region else self.aws_default_region, | ||
endpoint_url=self.endpoint_url) | ||
|
||
return client | ||
|
||
@staticmethod | ||
def get_timeout(timeout): | ||
if not timeout: | ||
timeout = "60,10" # default values | ||
try: | ||
|
||
if isinstance(timeout, int): | ||
read_timeout = timeout | ||
connect_timeout = 10 | ||
|
||
else: | ||
timeout_vals = timeout.split(',') | ||
read_timeout = int(timeout_vals[0]) | ||
# the default connect timeout is 10 | ||
connect_timeout = 10 if len(timeout_vals) == 1 else int(timeout_vals[1]) | ||
|
||
except ValueError: | ||
raise DemistoException("You can specify just the read timeout (for example 60) or also the connect " | ||
"timeout followed after a comma (for example 60,10). If a connect timeout is not " | ||
"specified, a default of 10 second will be used.") | ||
return read_timeout, connect_timeout | ||
|
||
register_module_line('AWSApiModule', 'end', __line__(), wrapper=1) | ||
### END GENERATED CODE ### |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete the generated code, this is not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@anas-yousef Done
Remove generated code
…tyCenter' into sepaprivate-contrib-AWS-IAMIdentityCenter
Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py
Outdated
Show resolved
Hide resolved
Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py
Outdated
Show resolved
Hide resolved
…AWSIAMIdentityCenter.py Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
…AWSIAMIdentityCenter.py Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
For the Reviewer: Successfully created a pipeline in Gitlab with url: https://code.pan.run/xsoar/content/-/pipelines/6364492 |
285f34c
into
demisto:contrib/xsoar-contrib_sepaprivate-contrib-AWS-IAMIdentityCenter
* [Marketplace Contribution] AWS - IAM Identity Center (#28559) * Update Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py * Update AWSIAMIdentityCenter.pyl * Update pack_metadata.json * Update AWSIAMIdentityCenter_description.md * Update AWSIAMIdentityCenter.ymll * Delete generated API module * Removed unnecessary package --------- Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> * Update AWSIAMIdentityCenter.yml * Update README.md * Update README.md * Fixed Pack README --------- Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
* [Marketplace Contribution] AWS - IAM Identity Center (demisto#28559) * Update Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py * Update AWSIAMIdentityCenter.pyl * Update pack_metadata.json * Update AWSIAMIdentityCenter_description.md * Update AWSIAMIdentityCenter.ymll * Delete generated API module * Removed unnecessary package --------- Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> * Update AWSIAMIdentityCenter.yml * Update README.md * Update README.md * Fixed Pack README --------- Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
* [Marketplace Contribution] AWS - IAM Identity Center (demisto#28559) * Update Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py * Update AWSIAMIdentityCenter.pyl * Update pack_metadata.json * Update AWSIAMIdentityCenter_description.md * Update AWSIAMIdentityCenter.ymll * Delete generated API module * Removed unnecessary package --------- Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> * Update AWSIAMIdentityCenter.yml * Update README.md * Update README.md * Fixed Pack README --------- Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
* [Marketplace Contribution] AWS - IAM Identity Center (#28559) * Update Packs/AWS-IAMIdentityCenter/Integrations/AWSIAMIdentityCenter/AWSIAMIdentityCenter.py * Update AWSIAMIdentityCenter.pyl * Update pack_metadata.json * Update AWSIAMIdentityCenter_description.md * Update AWSIAMIdentityCenter.ymll * Delete generated API module * Removed unnecessary package --------- Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com> * Update AWSIAMIdentityCenter.yml * Update README.md * Update README.md * Fixed Pack README --------- Co-authored-by: xsoar-bot <67315154+xsoar-bot@users.noreply.github.com> Co-authored-by: sepaprivate <113604678+sepaprivate@users.noreply.github.com> Co-authored-by: anas-yousef <44998563+anas-yousef@users.noreply.github.com>
Status
Contributor
@sepaprivate
Description
AWS IAM Identity Center
Author: Sameh El-Hakim
With AWS IAM Identity Center (successor to AWS Single Sign-On), you can manage sign-in security for your workforce identities, also known as workforce users. IAM Identity Center provides one place where you can create or connect workforce users and manage their access centrally across all their AWS accounts and applications. IAM Identity Center is the recommended approach for workforce authentication and authorization in AWS, for organizations of any size and type.
Video Link
Short demo video of the Pack usage. Speeds up the review. Optional but recommended. Use a video sharing service such as Google Drive or YouTube.