AWS Cross Account Delegation: IAM Users, Roles and Policies
Our Goal
We previously detailed how we set up our AWS multi-account organisation structure here. In this post, we will outline how we set up our access from our AWS Security account into our AWS Preprod and AWS Production accounts.
Note: In this post, anything written as a BaSH variable should be replaced with the value appropriate to you e.g. ${YOUR_NAME}
should be replaced with your actual name.

Why use cross-account roles?
Following Amazon’s best working practices we don’t want to use the root account for anything other than the absolute essentials e.g. adding a new organisation account or setting up reserved instances. For our daily activities, we want to create accounts following the least privilege model. This means the user should only be granted the access they require for the time they are completing that task.
Users and Groups
We only want to create users in our security account. If we created them in the organisational account they would have inherited permissions into the sub-accounts. Therefore creating them in one sub-account and granting access to another sub-account ensures all of our permissions are explicit and intended.
Like all organisations, we have several IT Admins who need to sign into the AWS Account and set things up. We don’t want to be adding roles or policies to individual users since this can easily lead to people collecting permissions without ever giving them back. Instead, we have created a group called “IT Admins” which we will add the users and apply our permissions to the group as a whole via policies.

Assuming Cross-Account Roles
For this demonstration, we will focus on the Security account (where we have our users in our Admin group) and the Preprod account.
In an incognito browser Sign into the Preprod account. This will be useful as in a minute we will also need to be signed into the Security account.
In the Preprod account, we are going to create a Role called TSU-IAM-ACCESS (You should pick a name appropriate to your organisation). Have a think about what access you think is appropriate for your user to do in the account. For us, we wanted this use to be able to view AWS resources, create and remove roles. We didn’t want our users doing any other actions since we had already decided that everything should be built using Terraform.
- We added “ReadOnlyAccess” to allow our users to look at all the AWS resources but not be able to apply changes.
- We also created an inline policy for our IAM permissions to allow our user to manage our roles. But most importantly we did not allow the user the ability to add users etc. If you are unsure use the visual editor’s multiple-select option which provides helpful hints.
- Against the inline policy, we also added the condition requiring 2MFA and our static IP address to make it more secure.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"iam:GetAccountPasswordPolicy",
"iam:ListPolicies",
...
...
"iam:ListUserTags"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "true"
},
"IpAddress": {
"aws:SourceIp": "${STATIC_IP_ADDRESS}"
}
}
}
]
}
For the trusted relationship, we added the users in the Admin group to the AWS Principle. Originally we wanted to add the group but found out this is not possible. So instead we decided to manage this role via Terraform to ensure the users in the group are always added to the different accounts. For now, you can go ahead and add-in your individual user.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::${SECURITY_ACCOUNT_ID}:user/User1"
]
},
"Action": "sts:AssumeRole",
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}
Now lets head back to the Security account and go to the Admin group. Under permissions, we need to add an Inline policy (Inline because we want the policy to only apply to the Admin accounts). In this policy, we are affiliating the access to the role created in the Preprod account.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::${PREPROD_ACCOUNT_ID}:role/TSU-IAM-ACCESS",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "true"
},
"IpAddress": {
"aws:SourceIp": "${STATIC_IP}"
}
}
}
]
}
Assuming the role
User1 should now be able to sign into the AWS Console against the Security Account. Once logged into will be able to assume into the other account. Clicking on the “Switch Role” next to the logout button, enter the Role and Account for the Preprod account. And that’s it. You have now assumed the role in the Preprod account. Repeat the steps above for each account that you want to have access too.

If you find yourself stuck on this page then it’s likely something isn’t quite right with your trusted relationships so go back and have another look.