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

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> ]
}
}
}
]
}

As a side note, there’s unfortunately not an aws:ResourceAccount key that would let you easily restrict which accounts are accessible on the resource side. You can make do with the resource ARN, of course, but it’s less clean. There is s3:ResourceAccount, which is important to know, as S3 bucket ARNs don’t have the account in them; read about it here.

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.