How do I export a report of IAM Identity Center identities and their assignments?

8 minute read
2

I want to see a list of all AWS IAM Identity Center permission sets and their respective principals for all accounts in my organization. Or, I want to see a list of all accounts of my organization and their individual permissions sets and principals.

Resolution

To see a list of provisioned permission sets and their respective principals that are assigned in IAM Identity Center, use a Python script. This action generates a JSON report that allows you to see the permissions that IAM Identity Center grants to different users or groups. Use the generated report's permission sets and principals to better govern and secure your environment.

Or, run a different Python script to retrieve the respective permission set and principal that IAM Identity Center maps to each account in your organization. This script generates the report as a .csv file.

Note: There's no native IAM Identity Center console feature, AWS Command Line Interface (AWS CLI) command, or API action that allows you to create these reports. Therefore, you must use the following scripts as a workaround.

Prerequisites

Before you proceed, verify the following prerequisites for your setup:

  1. Make sure that you're using the most recent version of the AWS CLI.
  2. Install or update the AWS SDK for Python and AWS CLI. For more information, refer the Quickstart guide to AWS Boto3.
  3. Properly configure the AWS CLI and the AWS SDK with working credentials. Use one of the following methods:
    Run the aws configure command on the AWS CLI.
    -or-
    Configure the AWS CLI or AWS Cloud Development Kit (AWS CDK) to get temporary access through an AWS Identity and Access Management (IAM) role.
  4. Make sure that you configured a named profile for the AWS CLI that has saved credentials for an IAM principal with the following permissions:
    Access to the AWS Organizations management account or the delegated administrator account for IAM Identity Center
    AWSSSOReadOnly and AWSSSODirectoryReadOnly, applied as AWS managed policies

Generate a list of permission sets and principals

This script generates a JSON report of all permission sets and the respective principals that are assigned to each permission set in IAM Identity Center. Under each permission set, the report lists the associated users and groups assigned for that permission set.

1.    Save the following script with a .py extension:
Note: In Step 3, your system saves the retrieved JSON file in the same directory.

import boto3, json

idstoreclient = boto3.client('identitystore')
ssoadminclient = boto3.client('sso-admin')
orgsclient= boto3.client('organizations')

users={}
groups={}
permissionSets={}
Accounts=[]

Instances= (ssoadminclient.list_instances()).get('Instances')
InstanceARN=Instances[0].get('InstanceArn')
IdentityStoreId=Instances[0].get('IdentityStoreId')

#Dictionary mapping User IDs to usernames
def mapUserIDs():
    ListUsers=idstoreclient.list_users(IdentityStoreId=IdentityStoreId)
    ListOfUsers=ListUsers['Users']
    while 'NextToken' in ListUsers.keys():
        ListUsers=idstoreclient.list_users(IdentityStoreId=IdentityStoreId,NextToken=ListUsers['NextToken'])
        ListOfUsers.extend(ListUsers['Users'])
    for eachUser in ListOfUsers:
        users.update({eachUser.get('UserId'):eachUser.get('UserName')})
mapUserIDs()

#Dictionary mapping Group IDs to display names
def mapGroupIDs():
    ListGroups=idstoreclient.list_groups(IdentityStoreId=IdentityStoreId)
    ListOfGroups=ListGroups['Groups']
    while 'NextToken' in ListGroups.keys():
        ListGroups=idstoreclient.list_groups(IdentityStoreId=IdentityStoreId,NextToken=ListGroups['NextToken'])
        ListOfGroups.extend(ListGroups['Groups'])
    for eachGroup in ListOfGroups:
        groups.update({eachGroup.get('GroupId'):eachGroup.get('DisplayName')})
mapGroupIDs()

#Dictionary mapping permission set ARNs to permission set names
def mapPermissionSetIDs():
    ListPermissionSets=ssoadminclient.list_permission_sets(InstanceArn=InstanceARN)
    ListOfPermissionSets=ListPermissionSets['PermissionSets']
    while 'NextToken' in ListPermissionSets.keys():
        ListPermissionSets=ssoadminclient.list_permission_sets(InstanceArn=InstanceARN,NextToken=ListPermissionSets['NextToken'])
        ListOfPermissionSets.extend(ListPermissionSets['PermissionSets'])
    for eachPermissionSet in ListOfPermissionSets:
        permissionSetDescription=ssoadminclient.describe_permission_set(InstanceArn=InstanceARN,PermissionSetArn=eachPermissionSet)
        permissionSetDetails=permissionSetDescription.get('PermissionSet')
        permissionSets.update({permissionSetDetails.get('PermissionSetArn'):permissionSetDetails.get('Name')})
mapPermissionSetIDs()

#Listing Permissionsets provisioned to an account
def GetPermissionSetsProvisionedToAccount(AccountID):
    ListOfPermissionSetsProvisionedToAccount=[]
    PermissionSetsProvisionedToAccount=ssoadminclient.list_permission_sets_provisioned_to_account(InstanceArn=InstanceARN,AccountId=AccountID)
    try:
        ListOfPermissionSetsProvisionedToAccount = PermissionSetsProvisionedToAccount['PermissionSets']
        while 'NextToken' in PermissionSetsProvisionedToAccount.keys():
            PermissionSetsProvisionedToAccount=ssoadminclient.list_permission_sets_provisioned_to_account(InstanceArn=InstanceARN,AccountId=AccountID,NextToken=PermissionSetsProvisionedToAccount['NextToken'])
            ListOfPermissionSetsProvisionedToAccount.extend(PermissionSetsProvisionedToAccount['PermissionSets'])
        return(ListOfPermissionSetsProvisionedToAccount)
    except:
        return(ListOfPermissionSetsProvisionedToAccount)


#To retrieve the assignment of each permissionset/user/group/account assignment
def ListAccountAssignments(AccountID):
    PermissionSetsList=GetPermissionSetsProvisionedToAccount(AccountID)
    Assignments=[]
    for permissionSet in PermissionSetsList:
        AccountAssignments=ssoadminclient.list_account_assignments(InstanceArn=InstanceARN,AccountId=AccountID,PermissionSetArn=permissionSet)
        Assignments.extend(AccountAssignments['AccountAssignments'])
        while 'NextToken' in AccountAssignments.keys():
            AccountAssignments=ssoadminclient.list_aaccount_assignments(InstanceArn=InstanceARN,AccountId=AccountID,PermissionSetArn=permissionSet,NextToken=AccountAssignments['NextToken'])
            Assignments.extend(AccountAssignments['AccountAssignments'])
    return(Assignments)

#To list all the accounts in the organization
def ListAccountsInOrganization():
    AccountsList=orgsclient.list_accounts()
    ListOfAccounts=AccountsList['Accounts']
    while 'NextToken' in AccountsList.keys():
        AccountsList=orgsclient.list_accounts(NextToken=AccountsList['NextToken'])
        ListOfAccounts.extend(AccountsList['Accounts'])
    for eachAccount in ListOfAccounts:
        Accounts.append(str(eachAccount.get('Id')))
    return(Accounts)

#To translate set datatype to json
class SetEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)
        return json.JSONEncoder.default(self, obj)

def GetListOfAssignmentsForPermissionSets():
    ListOfAccountIDs=ListAccountsInOrganization()
    entries=[]
    PermissionSetListForAssignments={}
    for eachAccountID in ListOfAccountIDs:
        GetAccountAssignments=ListAccountAssignments(eachAccountID)
        for eachAssignment in GetAccountAssignments:
            if(permissionSets.get(eachAssignment.get('PermissionSetArn'))) not in PermissionSetListForAssignments.keys():
                SetOfUsersandGroups={'Users':set(),'Groups':set()}
                PermissionSetListForAssignments[permissionSets.get(eachAssignment.get('PermissionSetArn'))]=SetOfUsersandGroups
            SetOfUsersandGroups=PermissionSetListForAssignments.get(permissionSets.get(eachAssignment.get('PermissionSetArn')))
            if(eachAssignment.get('PrincipalType')=='GROUP'):
                setOfGroups=SetOfUsersandGroups.get('Groups')
                setOfGroups.add(groups.get(eachAssignment.get('PrincipalId')))
                SetOfUsersandGroups.update({'Groups':setOfGroups})
                PermissionSetListForAssignments.update({permissionSets.get(eachAssignment.get('PermissionSetArn')):SetOfUsersandGroups})
            else:
                setOfUsers=SetOfUsersandGroups.get('Users')
                setOfUsers.add(users.get(eachAssignment.get('PrincipalId')))
                SetOfUsersandGroups.update({'Users':setOfUsers})
                PermissionSetListForAssignments.update({permissionSets.get(eachAssignment.get('PermissionSetArn')):SetOfUsersandGroups})
    with open("AssignmentsForPermissionSets.json", "w") as outfile:
        json.dump(PermissionSetListForAssignments, outfile, cls=SetEncoder)
    print("Done!AssignmentsForPermissionSets.json generated successfully!")
GetListOfAssignmentsForPermissionSets()

2.    Run this script in a Terminal (macOS) or PowerShell (Windows) window. This application must have the credentials of an IAM user or role with the permissions to access your IAM Identity Center configuration.

3.    You receive the output as a JSON file titled AssignmentsForPermissionSets. This contains the extracted information of all the users and groups that are assigned to all the permission sets in IAM Identity Center. Here's an example output:

{  "AdministratorAccess": {
    "Users": [
      "Charlie",
      "Ted"
    ],
    "Groups": [
      "Admins",
      "Developers"
    ]
  },
  "PowerUserAccess": {
    "Users": [
      "Chandler",
      "Joey"
    ],
    "Groups": [
      "Developers",
      "Testers"
    ]
  },
  "SystemAdministrator": {
    "Users": [
      "Sherlock"
    ],
    "Groups": [
      "DevOps"
    ]
  }
}

Note: A report that doesn't include a permission set implies that the permission set isn't provisioned to any account.

Generate a list of AWS Accounts and their respective principals and permission set assignments

This script generates a .csv report of all the AWS accounts in your organization. For each account, the report lists all permission sets and their respective principals that are assigned to the account through IAM Identity Center.

1.    Save the following script with a .py extension:
Note: In Step 3, your system saves the retrieved .csv file in the same directory:

import boto3, csv

idstoreclient = boto3.client('identitystore')
ssoadminclient = boto3.client('sso-admin')
orgsclient= boto3.client('organizations')

users={}
groups={}
permissionSets={}
Accounts={}

Instances= (ssoadminclient.list_instances()).get('Instances')
InstanceARN=Instances[0].get('InstanceArn')
IdentityStoreId=Instances[0].get('IdentityStoreId')


#Dictionary mapping User IDs to usernames
def mapUserIDs():
    ListUsers=idstoreclient.list_users(IdentityStoreId=IdentityStoreId)
    ListOfUsers=ListUsers['Users']
    while 'NextToken' in ListUsers.keys():
        ListUsers=idstoreclient.list_users(IdentityStoreId=IdentityStoreId,NextToken=ListUsers['NextToken'])
        ListOfUsers.extend(ListUsers['Users'])
    for eachUser in ListOfUsers:
        users.update({eachUser.get('UserId'):eachUser.get('UserName')})
mapUserIDs()

#Dictionary mapping Group IDs to display names
def mapGroupIDs():
    ListGroups=idstoreclient.list_groups(IdentityStoreId=IdentityStoreId)
    ListOfGroups=ListGroups['Groups']
    while 'NextToken' in ListGroups.keys():
        ListGroups=idstoreclient.list_groups(IdentityStoreId=IdentityStoreId,NextToken=ListGroups['NextToken'])
        ListOfGroups.extend(ListGroups['Groups'])
    for eachGroup in ListOfGroups:
        groups.update({eachGroup.get('GroupId'):eachGroup.get('DisplayName')})
mapGroupIDs()

#Dictionary mapping permission set ARNs to permission set names
def mapPermissionSetIDs():
    ListPermissionSets=ssoadminclient.list_permission_sets(InstanceArn=InstanceARN)
    ListOfPermissionSets=ListPermissionSets['PermissionSets']
    while 'NextToken' in ListPermissionSets.keys():
        ListPermissionSets=ssoadminclient.list_permission_sets(InstanceArn=InstanceARN,NextToken=ListPermissionSets['NextToken'])
        ListOfPermissionSets.extend(ListPermissionSets['PermissionSets'])
    for eachPermissionSet in ListOfPermissionSets:
        permissionSetDescription=ssoadminclient.describe_permission_set(InstanceArn=InstanceARN,PermissionSetArn=eachPermissionSet)
        permissionSetDetails=permissionSetDescription.get('PermissionSet')
        permissionSets.update({permissionSetDetails.get('PermissionSetArn'):permissionSetDetails.get('Name')})
mapPermissionSetIDs()

#Listing Permissionsets provisioned to an account
def GetPermissionSetsProvisionedToAccount(AccountID):
    PermissionSetsProvisionedToAccount=ssoadminclient.list_permission_sets_provisioned_to_account(InstanceArn=InstanceARN,AccountId=AccountID)
    ListOfPermissionSetsProvisionedToAccount = PermissionSetsProvisionedToAccount['PermissionSets']
    while 'NextToken' in PermissionSetsProvisionedToAccount.keys():
        PermissionSetsProvisionedToAccount=ssoadminclient.list_permission_sets_provisioned_to_account(InstanceArn=InstanceARN,AccountId=AccountID,NextToken=PermissionSetsProvisionedToAccount['NextToken'])
        ListOfPermissionSetsProvisionedToAccount.extend(PermissionSetsProvisionedToAccount['PermissionSets'])    return(ListOfPermissionSetsProvisionedToAccount)

#To retrieve the assignment of each permissionset/user/group/account assignment
def ListAccountAssignments(AccountID):
    PermissionSetsList=GetPermissionSetsProvisionedToAccount(AccountID)
    Assignments=[]
    for permissionSet in PermissionSetsList:
        AccountAssignments=ssoadminclient.list_account_assignments(InstanceArn=InstanceARN,AccountId=AccountID,PermissionSetArn=permissionSet)
        Assignments.extend(AccountAssignments['AccountAssignments'])
        while 'NextToken' in AccountAssignments.keys():
            AccountAssignments=ssoadminclient.list_aaccount_assignments(InstanceArn=InstanceARN,AccountId=AccountID,PermissionSetArn=permissionSet,NextToken=AccountAssignments['NextToken'])
            Assignments.extend(AccountAssignments['AccountAssignments'])
    return(Assignments)


#To list all the accounts in the organization
def ListAccountsInOrganization():
    AccountsList=orgsclient.list_accounts()
    ListOfAccounts=AccountsList['Accounts']
    while 'NextToken' in AccountsList.keys():
        AccountsList=orgsclient.list_accounts(NextToken=AccountsList['NextToken'])
        ListOfAccounts.extend(AccountsList['Accounts'])
    for eachAccount in ListOfAccounts:
        Accounts.update({eachAccount.get('Id'):eachAccount.get('Name')})
    return(Accounts)

def WriteToExcel():
    Accounts=ListAccountsInOrganization()
    ListOfAccountIDs=list(Accounts.keys())
    entries=[]
    for eachAccountID in ListOfAccountIDs:
        try:
            GetAccountAssignments=ListAccountAssignments(eachAccountID)
            for eachAssignment in GetAccountAssignments:
                entry=[]
                entry.append(eachAssignment.get('AccountId'))
                entry.append(Accounts.get(eachAssignment.get('AccountId')))
                entry.append(permissionSets.get(eachAssignment.get('PermissionSetArn')))
                entry.append(eachAssignment.get('PrincipalType'))
                if(eachAssignment.get('PrincipalType')=='GROUP'):
                    entry.append(groups.get(eachAssignment.get('PrincipalId')))
                else:
                    entry.append(users.get(eachAssignment.get('PrincipalId')))
                entries.append(entry)
        except:
            continue
    filename = "IdentityStoreReport.csv"
    headers=['Account ID', 'Account Name', 'Permission Set','Principal Type', 'Principal']

    with open(filename, 'w') as report:
        csvwriter = csv.writer(report)
        csvwriter.writerow(headers)
        csvwriter.writerows(entries)
    print("Done! 'IdentityStoreReport.csv' report is generated successfully!")
WriteToExcel()

2.    Run this script in a terminal or PowerShell window. This application must have the credentials of an IAM user or role with the permissions to access your IAM Identity Center configuration.

3.    You receive the output as a .csv file titled IdentityStoreReport. This contains a tabulated view, by AWS account and of all the assignments in IAM Identity Center. Headers include Account ID, Account Name, Permission Set, Principal Type, and Principal Name.
Note: A report that doesn't include an account implies that no permission set was provisioned to that account.
Here's an example output:

Account IDAccount NamePermission SetPrincipal TypePrincipal
123456789012DevelopmentPowerUserAccessGROUPDevelopers
123456789012DevelopmentPowerUserAccessUSERRoss
123456789012DevelopmentAdministratorAccessUSERPhoebe
123456789012DevelopmentSystemAdministratorUSERJake
345678901234ProductionAdministratorAccessGROUPAdmins
345678901234ProductionAdministratorAccessGROUPTesting
901234567890StagingPowerUserAccessGROUPTesting
901234567890StagingAdministratorAccessGROUPClient
901234567890StagingPowerUserAccessUSERGina
901234567890StagingPowerUserAccessGROUPAdmins

Considerations and limitations

To avoid issues when you run this script, note the following considerations and limitations:

  • IAM Identity Center APIs have a collective throttle maximum of 20 transactions per second (TPS). For more information, see IAM Identity Center throttle limits. You might encounter the exceptions RequestLimitExceeded and ThrottlingException in large environments. In this case, implement strategies to manage API throttling.
  • If an AWS Account isn't in the report, then no permission set was provisioned to that account.
  • The more accounts that you have in your setup, the more time it takes to generate the report.
AWS OFFICIAL
AWS OFFICIALUpdated 7 months ago
7 Comments

Thanks for the useful scripts!

In each of the scripts, there is a typo in the function ListAccountsInOrganization.

In the while loop, the ListOfAccounts var should be updated by either the += operator or the extend method, instead of just =, so that full list of accounts doesn't just include whatever is on the last set retrieved from the NextToken list.

The updated line is here:

def ListAccountsInOrganization():
    AccountsList=orgsclient.list_accounts()
    ListOfAccounts=AccountsList['Accounts']
    while 'NextToken' in AccountsList.keys():
        AccountsList=orgsclient.list_accounts(NextToken=AccountsList['NextToken'])
        ListOfAccounts+=AccountsList['Accounts']
    for eachAccount in ListOfAccounts:
        Accounts.update({eachAccount.get('Id'):eachAccount.get('Name')})
    return(Accounts)
B
replied 7 months ago

Thank you for your comment. We'll review and update the Knowledge Center article as needed.

profile pictureAWS
MODERATOR
replied 7 months ago

Incredible work!! Just what I was looking for. Would really appreciate a GUI feature but these scripts saved the day for now.

Sanders
replied 7 months ago

Will have a go at fixing myself but I'm no Python expert

Each script is generating an error for me

File "/Users/mark.lloyd/report2.py", line 75 def ListOfAccounts=AccountsList['Accounts']: ^ SyntaxError: expected '('

Mark
replied 7 months ago

Thank you for your comment. We'll review and update the Knowledge Center article as needed.

profile pictureAWS
MODERATOR
replied 7 months ago

Thanks for the article. Looks like the output of the second script is somehow being limited, I don't get full list of permission sets in all accounts.

Michal
replied 5 months ago

Thank you for your comment. We'll review and update the Knowledge Center article as needed.

profile pictureAWS
MODERATOR
replied 5 months ago