Filtering AWS Identity Store Users by Status: A Complete Guide
AWS IAM Identity Center now supports user status filtering through the Identity Store API. This comprehensive guide walks you through building a Python automation script to list, filter, and audit users by their ENABLED or DISABLED status across single or multiple AWS accounts. Perfect for security audits, compliance reporting, and large-scale account cleanup operations.
Introduction
Managing user accounts at scale in AWS IAM Identity Center can be challenging, especially when dealing with large-scale account cleanup operations. Organizations often face scenarios where thousands of user accounts need to be audited, and determining which accounts are active versus disabled becomes critical for security and compliance.
The Challenge:
Consider a common scenario: An organization discovers a large number of user accounts that were inadvertently created in their IAM Identity Center. These accounts exist in the root billing account where Identity Center is configured, and the team needs to:
- Audit all user accounts to understand their current status
- Identify which accounts are enabled versus disabled
- Clean up inactive or unnecessary accounts safely
- Proceed carefully given the scale and potential impact on operations
Previously, administrators faced a significant limitation: while the Identity Store API provided user information, there was no way to determine if an account was enabled or disabled. This made large-scale audits and cleanup operations extremely difficult and error-prone.
The Solution:
AWS IAM Identity Center recently added a highly requested feature: the
UserStatus field in the Identity Store API. This field now appears in both ListUsers and DescribeUser API responses, making it possible to programmatically audit user account status across your organization.In this guide, we'll walk through building a Python automation script that:
- Lists all users with their current status (ENABLED/DISABLED)
- Filters users based on their status for targeted audits
- Scales to handle large user populations efficiently
- Works across multiple AWS accounts in an organization structure
This solution enables the systematic audit and cleanup operations that were previously impossible, helping organizations maintain security hygiene at scale.
What's New in the Identity Store API?
The Identity Store API now returns a
UserStatus field with these possible values:- ENABLED: User can authenticate and access resources
- DISABLED: User account is blocked from authentication
- UNKNOWN: Status information unavailable (legacy users)
Important Limitation
While the API returns status information, it does not support server-side filtering by status. This means you need to:
- Call
ListUsersto retrieve all users - Filter the results client-side based on the
UserStatusfield
This approach is perfectly acceptable for most use cases, as Identity Store user lists are typically manageable in size.
The Solution: A Python Automation Script
Let's build a script that handles both single-account and multi-account scenarios.
Design Goals
- Auto-discovery: Automatically find the Identity Store ID
- Flexible filtering: Support filtering by ENABLED/DISABLED status
- Organization support: Scan multiple accounts in AWS Organizations
- Simple interface: Minimal command-line arguments
Process Overview

Key Components:
- Auto-Discovery: Finds Identity Store ID via SSO Admin API
- Pagination Handler: Processes large user lists efficiently
- Client-Side Filter: Filters by UserStatus after retrieval
- Cross-Account Access: Uses STS AssumeRole for organization scanning
Implementation
Step 1: Core Functions
Let's start with the essential building blocks:
1
2
3
4
5
6
7
8
import boto3
import sys
def get_identity_store_id(session):
"""Auto-discover Identity Store ID from SSO instance."""
sso = session.client('sso-admin')
instances = sso.list_instances()['Instances']
return instances[0]['IdentityStoreId'] if instances else NoneThis function eliminates the need for users to manually provide the Identity Store ID—a common pain point in automation scripts.
Step 2: User Listing with Pagination
The Identity Store API uses pagination for large user lists. Here's how we handle it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def list_users(session, identity_store_id, status_filter=None):
"""List users with optional status filtering."""
client = session.client('identitystore')
paginator = client.get_paginator('list_users')
users = []
for page in paginator.paginate(IdentityStoreId=identity_store_id):
for user in page['Users']:
status = user.get('UserStatus', 'UNKNOWN')
# Client-side filtering
if not status_filter or status == status_filter:
users.append({
'UserName': user['UserName'],
'Status': status,
'DisplayName': user.get('DisplayName', 'N/A')
})
return usersNotice the client-side filtering logic—we check each user's status after retrieval since the API doesn't support filtering parameters.
Step 3: Organization Support
For enterprises managing multiple AWS accounts, we need cross-account access:
1
2
3
4
5
6
7
8
9
10
def get_org_accounts():
"""Get all active accounts in the organization."""
org = boto3.client('organizations')
accounts = []
paginator = org.get_paginator('list_accounts')
for page in paginator.paginate():
accounts.extend([a for a in page['Accounts'] if a['Status'] == 'ACTIVE'])
return accountsThe script assumes the
OrganizationAccountAccessRole exists in member accounts—a standard role created by AWS Organizations.Step 4: Main Logic
Bringing it all together with a clean command-line interface that handles both single-account and organization modes. The complete implementation is provided at the end of this guide.
Usage Examples
Single Account Scenarios
List all users with their status:
1
python list_users_by_status.pyOutput:
1
2
3
4
5
6
7
8
Found 7 user(s):
john.doe@example.com ENABLED John Doe
jane.smith@example.com ENABLED Jane Smith
bob.wilson@example.com ENABLED Bob Wilson
alice.chen@example.com ENABLED Alice Chen
mike.johnson@example.com ENABLED Mike Johnson
sarah.davis@example.com ENABLED Sarah Davis
former.employee@example.com DISABLED Former EmployeeFind only disabled users:
1
python list_users_by_status.py DISABLEDOutput:
1
2
Found 1 user(s):
former.employee@example.com DISABLED Former EmployeeOrganization-Wide Scenarios
Scan all accounts for disabled users:
1
python list_users_by_status.py DISABLED --orgOutput:
1
2
3
4
5
6
7
Scanning 5 accounts in organization...
Account: Production (123456789012)
old.contractor.com DISABLED Former Contractor
Account: Development (234567890123)
test.user.com DISABLED Test AccountThis is particularly useful for compliance audits where you need to verify that disabled accounts exist across your entire organization.
Real-World Use Cases
1. Security Audits
Quickly identify all disabled accounts across your organization to ensure former employees or contractors no longer have access:
1
python list_users_by_status.py DISABLED --org > disabled_users_audit.txt2. Compliance Reporting
Generate regular reports showing the status of all user accounts for compliance teams:
1
2
# Weekly report
python list_users_by_status.py --org > weekly_user_status_$(date +%Y%m%d).txt3. Account Cleanup
Find disabled accounts that can be safely deleted after a retention period:
1
python list_users_by_status.py DISABLED > accounts_to_review.txt4. Onboarding Verification
Verify that newly created accounts are properly enabled:
1
python list_users_by_status.py ENABLED | grep "new.hire@example.com"Best Practices
1. Use Virtual Environments
Always isolate your Python dependencies to avoid conflicts with system packages. This follows Python best practices and prevents dependency issues.
1
2
3
python3 -m venv .venv
source .venv/bin/activate
pip install boto32. Secure Credential Management
Never hardcode credentials in your scripts. AWS recommends the following credential chain (in order of precedence):
- IAM roles (recommended for EC2/Lambda/ECS execution)
- AWS CLI configuration (
~/.aws/credentialsand~/.aws/config) - Environment variables (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY) - AWS SSO login for temporary credentials
For more information, see AWS Security Best Practices .
3. Implement Proper Error Handling
For production use, implement comprehensive error handling for common AWS exceptions:
AccessDeniedException- Verify IAM permissionsResourceNotFoundException- Check Identity Store IDThrottlingException- Implement exponential backoff retry logic
The complete script at the end of this guide includes proper error handling.
4. Handle API Throttling
For large organizations, implement exponential backoff to handle API throttling gracefully. AWS API Gateway and services use the token bucket algorithm for throttling. When you exceed limits, you'll receive
429 Too Many Requests or ThrottlingException errors.The complete script includes retry logic with exponential backoff. For more information, see AWS Well-Architected Framework - Throttle Requests .
5. Apply Least Privilege Permissions
Grant only the minimum permissions required for the script to function. Avoid using overly permissive policies like
AdministratorAccess.Required permissions for single-account mode:
sso:ListInstancesidentitystore:ListUsers
Additional permissions for organization mode:
organizations:ListAccountssts:AssumeRole
This follows the AWS IAM least privilege principle . See the IAM Permissions section in the USER_GUIDE.md for complete policy examples.
Troubleshooting
"No Identity Store found"
Cause: IAM Identity Center is not enabled in the account.
Solution: Enable IAM Identity Center in the AWS Console under "IAM Identity Center" service.
"Access denied" in organization mode
Cause: Missing
OrganizationAccountAccessRole or insufficient permissions.Solution:
- Verify the role exists in member accounts
- Check your IAM user/role has
sts:AssumeRolepermission - Ensure the trust policy allows your account to assume the role
"Module not found: boto3"
Cause: boto3 not installed or virtual environment not activated.
Solution:
1
2
source .venv/bin/activate
pip install boto3Performance Considerations
Pagination Efficiency
The script uses boto3's built-in pagination, which automatically handles the
NextToken parameter for retrieving subsequent pages. According to AWS documentation:- Identity Store ListUsers API: Returns paginated results with a default page size
- MaxResults parameter: Can be specified to control page size (check API limits)
- Automatic handling: boto3 paginators abstract away the complexity of token management
The pagination is efficient and handles user lists of any size without loading all results into memory at once.
API Throttling Limits
Be aware of AWS Organizations and Identity Store API throttling limits:
- AWS Organizations APIs: Have specific throttling limits for account management operations (see AWS Organizations Quotas )
- Identity Store APIs: Have throttling limits that can be increased by opening a support case (see IAM Identity Center Quotas )
- Token bucket algorithm: AWS uses this algorithm for API throttling - when limits are exceeded, you'll receive
ThrottlingExceptionerrors
For large-scale operations, implement exponential backoff retry logic (shown in Best Practices section).
Organization Scanning Time
Scanning time depends on:
- Number of accounts in your organization
- Number of users per account
- API throttling limits and retry delays
- Network latency to AWS endpoints
Typical performance: For an organization with 10 accounts and 100 users each, expect approximately 30-60 seconds total runtime, assuming no throttling.
Optimization Strategies
- Parallel Processing: For very large organizations (50+ accounts), consider using Python's
concurrent.futuresto scan multiple accounts in parallel, while respecting API rate limits - Caching: Cache Identity Store IDs to avoid repeated
ListInstancesAPI calls across multiple script executions - Targeted Scanning: If you know which accounts have IAM Identity Center enabled, maintain a list and skip accounts without it
- Batch Operations: Group operations where possible to minimize API calls
- Monitor Throttling: Track
ThrottlingExceptionoccurrences and adjust concurrency or add delays accordingly
Future Enhancements
While this script covers the essentials, here are some ideas for extension:
- JSON Output: Add
--jsonflag for machine-readable output - CSV Export: Generate CSV files for spreadsheet analysis
- Email Notifications: Send alerts when disabled accounts are found
- CloudWatch Integration: Push metrics to CloudWatch for monitoring
- Lambda Deployment: Run as a scheduled Lambda function for automated audits
Conclusion
The addition of
UserStatus to the Identity Store API is a welcome improvement for AWS administrators. While server-side filtering isn't available yet, client-side filtering is straightforward and performant for typical use cases.This script provides a solid foundation for user status management across single and multi-account environments. Whether you're conducting security audits, generating compliance reports, or simply keeping track of your user base, this tool streamlines the process.
Complete Script
Save this as
list_users_by_status.py:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#!/usr/bin/env python3
"""List AWS Identity Store users by status across organization accounts."""
import boto3
import sys
def get_org_accounts():
"""Get all accounts in the organization."""
org = boto3.client('organizations')
accounts = []
paginator = org.get_paginator('list_accounts')
for page in paginator.paginate():
accounts.extend([a for a in page['Accounts'] if a['Status'] == 'ACTIVE'])
return accounts
def get_identity_store_id(session):
"""Get Identity Store ID from SSO instance."""
sso = session.client('sso-admin')
instances = sso.list_instances()['Instances']
return instances[0]['IdentityStoreId'] if instances else None
def list_users(session, identity_store_id, status_filter=None):
"""List users from Identity Store."""
client = session.client('identitystore')
paginator = client.get_paginator('list_users')
users = []
for page in paginator.paginate(IdentityStoreId=identity_store_id):
for user in page['Users']:
status = user.get('UserStatus', 'UNKNOWN')
if not status_filter or status == status_filter:
users.append({
'UserName': user['UserName'],
'Status': status,
'DisplayName': user.get('DisplayName', 'N/A')
})
return users
def main():
status_filter = sys.argv[1] if len(sys.argv) > 1 and sys.argv[1] in ['ENABLED', 'DISABLED'] else None
org_mode = '--org' in sys.argv
if org_mode:
accounts = get_org_accounts()
print(f"Scanning {len(accounts)} accounts in organization...\n")
for account in accounts:
try:
sts = boto3.client('sts')
role_arn = f"arn:aws:iam::{account['Id']}:role/OrganizationAccountAccessRole"
creds = sts.assume_role(RoleArn=role_arn, RoleSessionName='UserStatusCheck')['Credentials']
session = boto3.Session(
aws_access_key_id=creds['AccessKeyId'],
aws_secret_access_key=creds['SecretAccessKey'],
aws_session_token=creds['SessionToken']
)
identity_store_id = get_identity_store_id(session)
if not identity_store_id:
continue
users = list_users(session, identity_store_id, status_filter)
if users:
print(f"Account: {account['Name']} ({account['Id']})")
for user in users:
print(f" {user['UserName']:<30} {user['Status']:<10} {user['DisplayName']}")
print()
except Exception as e:
print(f" Skipped {account['Name']}: {str(e)}\n")
else:
session = boto3.Session()
identity_store_id = get_identity_store_id(session)
if not identity_store_id:
print("No Identity Store found")
sys.exit(1)
users = list_users(session, identity_store_id, status_filter)
print(f"Found {len(users)} user(s):")
for user in users:
print(f" {user['UserName']:<30} {user['Status']:<10} {user['DisplayName']}")
if __name__ == '__main__':
main()Additional Resources
- AWS Identity Store API Reference
- AWS Organizations Documentation
- IAM Identity Center Best Practices
- boto3 Documentation
Questions or feedback? Reach out to your AWS account team or AWS Support for assistance.
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article