The AWS Idendity Center is the successor to AWS Single Sign-On. The (most probably refactored) service launched some new API calls - however, my biggest pain point couldn´t be resolved:
How can I retrieve all account assignments independently? You have to know a PermissionSetArn and can only retrieve a subset of the currently active assignments.
The Problem (business case)
As an administrator for hundreds of AWS Accounts I want to be able to gain full visibility about the currently active users/groups. As a best practice, I recommend deploying all your permissions via IaC or some kind of automation. In my opinion, the best setup is to manage all my group bindings via Terraform and only use direct user bindings when necessary. But you never know what happens behind the scenes. In order to prevent any unwanted account binding to be active you should implement the following controls:
Don´t allow any humans to deploy users/groups manually in your productive environment
Analyze Cloudtrail logs and send a notification to you SoC team or similar to be aware about assignments created by humans
Use Cloudtrail logs to inform your SoC team whenever a high privileged (root / break-glass / administrator) login happened
From time to time: Get an export of your active account assignments and check it against your codebase
This blog article shows you how I´ve implemented point (4)
Exploring the new API capabilities
Before I started to script a solution I spent some time finding the most elegant way to retrieve the account bindings. My goal was it to create a comprehensive overview that could be read by both machines and humans.
Soon I found out that the shining new service still had some legacy APIs active. In general, AWS has split the "Identity Part" into the IdentityStore API family and the authentication and authorization part into the newly available ssoadmin API family. For my use case I´ve found out that I need the following permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "GetAllSSOAccountAssignments",
"Effect": "Allow",
"Action": [
"organizations:ListAccounts",
"identitystore:DescribeUser",
"identitystore:DescribeGroup",
"sso:ListInstances",
"sso:ListPermissionSets",
"sso:ListAccountsForProvisionedPermissionSet",
"sso:DescribePermissionSet",
"sso:ListAccountAssignments"
],
"Resource": "*"
}
]
}
After some dry tests via the aws cli I´ve figured out how the access can be retrieved:
Get an overview of all accounts
Get an overview of all permission sets
Find out on which accounts a permission set is active
Get the account assignments for each account
Lazy load the Users and Groups
Wrapping all together via python scripts
The last part was quite easy - As I´ve known exactly how everything is connected i just thought which objects I need and how I want to connect them. In my case I´ve went with the json format instead of building class instances.
An example script is available on my github account.
The example script iterates over all AWS Accounts and prints out the account assignments for all 'USER' Objects. Feel free to set the log level to "INFO" in order to see which api calls are used to retrieve the information.
Challenges during the development
During the development, I´ve encountered some small stepping stones.
I´ve tested the script initially with the ReadOnlyAccess policy from AWS. Unfortunately I have discovered that AWS didn´t update their policies yet which resulted in failures.
ReadOnlyAccess is outdated. The identitystore:describe* and identitystore:list* actions aren´t available
Rest Assured: I have already informed AWS and they will update the policy in near future. Fun-fact: The console uses a different call to retrieve users and groups which works fine, but is not available on boto3 :).
Some API calls didn´t support pagination. Whenever possible I try to use the features coming with the vendors. Pagination is a nice feature when you want to load all objects from a list action in AWS. However, in my case I had to build my own solution for some calls as they didn´t were available in boto3.
MaxResults behavior is different for API calls. Sometimes the value was documented in the boto3 documentation, sometimes I had to call the API with a too high value and sometimes I had to do try and error in order to find out the max value.
The sso:ListInstances call is somehow strange. From my knowledge it isn´t possible to connect multiple identitystores in the identitycenter console. And this makes sense: I do not want to have multiple sources of truth when it comes to Identities for my organization. So why do I get an array back from AWS? Maybe I´ve missed something and this is technical possible - Anyway, in my case, I am just retrieving the first instance if nothing is provided during the initialization. Fun-fact: The api call fails with SSOAdmin.Client.exceptions.AccessDeniedException if no instance is configured. The error message is somehow missleading.
Comments