Use aws:PrincipalAccount to fine-tune your AWS SSO permission sets

Ben Kehoe
3 min readJul 23, 2021

Update 2023–03–21: Updated to reflect that the aws:ResourceAccount context key was added last year, and OrgID/OrgPaths context keys.

AWS SSO permission sets are a collection of policies that get attached to a managed IAM role in every account the permission set is provisioned to (that is, a principal — a user or group — is given that permission set in that account). While the IAM role has a complex name, with the prefix AWSReservedSSO, followed by the permission set name, followed by a random tag, each separated with an underscore, when the user is signing in, the “role name” they see is just the permission set name.

This means you want to create permission sets that apply to multiple accounts as part of the same “role” that the user is going to use. For example, you may Shopping Cart service, with a set of dev/test/prod accounts, and you want a CartDeveloper role that has access to all of them. But often you want different access in each account. But the same IAM policy is applied to the role in each account, so how can the permissions be different?

Enter the aws:PrincipalAccount context key. The value of this key when an IAM policy is being evaluated is the account id of the principal (that is, it will not change even if the access is to a resource in another account). This means an IAM policy statement can be conditional on whether or not it’s in a particular account. We’ll use the string operators in our conditions.

Let’s say we want to give CartDeveloper read permissions to DynamoDB in all accounts, and write only in dev. Our policy might look like this (for brevity, I’m leaving out actions like batching and transactions):

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DDBReadEverywhere",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:Query",
"dynamodb:Scan"
],
"Resource": "*"
},
{
"Sid": "DDBWriteInDev",
"Effect": "Allow",
"Action": "dynamodb:PutItem",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalAccount": "<dev account id>"
}
}
}
]
}

Alternatively, imagine we wanted to give an Admin role permissions to do everything in most accounts, but a narrow range of permissions, say, just CloudTrail in just a small number of accounts. We could use a policy like this:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AdminEverywhere",
"Effect": "Allow",
"Action": "*:*",
"Resource": "*"
},
{
"Sid": "ExceptThesePlaces",
"Effect": "Deny",
"NotAction": "cloudtrail:*",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:PrincipalAccount": [ <special accounts> ]
}
}
}
]
}

There is also an aws:ResourceAccount context key that lets you restrict which accounts are accessible on the resource side. This is not as useful for Identity Center control, as generally I recommend not doing a lot of cross-account access with an Identity Center role; instead, have the same or a separate permission set provisioned to the other account, and use both concurrently.

Finally, you can also have conditions that don’t depend on the account, but rather the OU within AWS Organizations: aws:PrincipalOrgPaths. It’s a bit complicated and the scope is beyond this article. You can also have a condition on the Organization itself, but as AWS SSO is tied to an organization, that’s not useful in permission sets. There are complementary resource conditions as well.

Hopefully this provides a useful framework for reducing the number of permission sets you need for a comprehensive security posture across your accounts with AWS SSO. As always, if you’ve got questions, hit me up on Twitter.

--

--