How to audit IAM role trust policies
Topic: Accounts access
Summary
Review and tighten IAM role trust policies: who can assume the role, under what conditions, and whether trust is least privilege. Use get-role and inspect AssumeRolePolicyDocument; remove overly broad principals and add conditions (e.g. MFA, source ARN) where appropriate.
Intent: How-to
Quick answer
- For each role, get the trust policy with get-role and inspect AssumeRolePolicyDocument; list principals (AWS, Service, Federated) and any Conditions.
- Ensure service roles (EC2, Lambda) trust only the intended service principal; ensure user-assumable roles trust specific users/roles or accounts, not wildcards, and add MFA or source conditions if required.
- Remove overly broad trust (e.g. Principal "*", or account-wide trust without conditions); document and test after changes so legitimate assume-role still works.
Prerequisites
Steps
-
List roles and fetch trust policies
List IAM roles with list-roles; for each role, call get-role and extract AssumeRolePolicyDocument; store or review in a consistent format.
-
Classify and evaluate principals
For each trust statement, identify Principal (Service, AWS, Federated); for AWS and Federated, check if it is account-wide, role/user-specific, or external; flag wildcards or overly broad trust.
-
Check conditions
Review Condition blocks (e.g. StringEquals for MFA, source ARN, or IP); ensure sensitive roles require MFA or scope; add conditions where missing for least privilege.
-
Tighten and verify
Update trust policies to restrict principals and add conditions; use assume-role (or the service that uses the role) to verify legitimate access still works and unauthorized assume is denied.
Summary
You will audit IAM role trust policies by listing roles, fetching their AssumeRolePolicyDocument, evaluating principals and conditions, and tightening trust to least privilege. Use this to reduce the risk of role assumption by unintended principals and to meet compliance.
Prerequisites
- IAM permissions to list and get roles (iam:ListRoles, iam:GetRole).
- Understanding of which principals (users, roles, accounts, services) should be allowed to assume each role.
Steps
Step 1: List roles and fetch trust policies
aws iam list-roles --query 'Roles[].[RoleName,Arn]' --output text
For each role:
aws iam get-role --role-name ROLE_NAME --query 'Role.AssumeRolePolicyDocument'
Save the JSON for review. You can script this to dump all trust policies to a file.
Step 2: Classify and evaluate principals
For each statement in the trust policy:
- Principal Service (e.g. ec2.amazonaws.com, lambda.amazonaws.com): Correct for service roles; ensure it is the right service and not a wildcard.
- Principal AWS (account or role/user ARN): Prefer specific role or user ARNs over account root (arn:aws:iam::123456789012:root allows any principal in the account). Flag account-root trust without conditions.
- Principal Federated: Used for IdP/SAML/OIDC; ensure the provider and audience are correct and scope is limited (e.g. attribute condition).
Flag: Principal: "*", account root without conditions, or trust of an entire external account without restrictions.
Step 3: Check conditions
Common conditions for assume-role:
- MFA:
Condition: { Bool: { "aws:MultiFactorAuthPresent": "true" } }so only MFA-authenticated sessions can assume the role. - Source ARN (e.g. Lambda):
StringEquals: { "lambda:FunctionArn": "arn:aws:lambda:..." }so only that function can assume the role. - External ID: For cross-account, require the correct external ID to avoid confused deputy.
Add conditions where the role is sensitive (e.g. admin role, cross-account role).
Step 4: Tighten and verify
Update the trust policy with put-role-policy (note: trust policy is updated with update-assume-role-policy):
aws iam update-assume-role-policy --role-name ROLE_NAME --policy-document file://trust-policy.json
After changes:
- Have an authorized user or service assume the role and run a minimal action; confirm it works.
- Attempt assume from an unauthorized principal (e.g. different user or role); confirm it is denied.
Verification
- All roles have trust policies reviewed; overly broad principals are removed or restricted.
- Sensitive roles have conditions (MFA, source ARN, or external ID) where required.
- Legitimate assume-role still succeeds; unauthorized assume is denied and documented.
Troubleshooting
update-assume-role-policy fails — Ensure the JSON is valid and the role name is correct. The trust policy must have at least one statement allowing sts:AssumeRole; you cannot remove the last principal.
Service can no longer assume role — If you added a Condition (e.g. lambda:FunctionArn), ensure the ARN matches exactly (region, account, function name). For EC2, the principal is the service; do not add a condition that blocks the service.
Cross-account assume broken — Verify the external account’s principal (role ARN or account ID) is still in the trust policy and that ExternalId (if used) matches what the other account passes.