AWS IAM Trust Policy Generator
Generate AWS IAM Trust Policy JSON for Lambda, EC2, ECS, and other services. Defines who can assume the role.
AWS IAM trust policies: who is allowed to assume the role
A trust policy is a JSON document attached to an IAM Role that answers a single question: "who is allowed to assume this role?". It is distinct from a permission policy, which answers "what is this role allowed to do once assumed". Every IAM role has exactly one trust policy and one or more permission policies — both are mandatory for the role to be useful.
Trust policies look like normal IAM policies but they always have a Principal field (identity-based policies do not) and the Action is almost always sts:AssumeRole (or sts:AssumeRoleWithSAML / sts:AssumeRoleWithWebIdentity for federated principals).
Anatomy of a trust policy
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
The example above is the typical EC2 instance-profile trust: EC2 is allowed to call sts:AssumeRole on this role so it can hand temporary credentials to a running instance. Swap ec2.amazonaws.com for lambda.amazonaws.com and you have a Lambda execution role; for ecs-tasks.amazonaws.com, an ECS task role.
Principal types
- Service: an AWS service principal —
ec2.amazonaws.com,lambda.amazonaws.com,ecs-tasks.amazonaws.com, etc. Used for instance profiles and execution roles. - AWS account:
"AWS": "arn:aws:iam::ACCOUNT_ID:root"trusts any principal in that account, or a specific user/role ARN to scope it down. Core building block for cross-account access. - Federated: SAML or OIDC identity providers — used by GitHub Actions OIDC, Auth0, Okta, Google Workspace. The
Actionbecomessts:AssumeRoleWithSAMLorsts:AssumeRoleWithWebIdentity. - CanonicalUser: legacy S3 canonical user ID — rarely used in new policies.
Conditions: the security guardrails
sts:ExternalId— mandatory for SaaS vendor cross-account access. Prevents the confused-deputy attack where a vendor is tricked into operating on the wrong customer's account.aws:PrincipalOrgID— restricts assumption to principals inside your AWS Organization.aws:SourceAccount+aws:SourceArn— confused-deputy prevention when a service (like S3 or SNS) calls another on your behalf.token.actions.githubusercontent.com:sub— for GitHub Actions OIDC, locks the role to a specific repo / branch / environment via the OIDC token's subject claim.aws:MultiFactorAuthPresent— requires MFA on the calling session beforeAssumeRolesucceeds.
Cross-account access pattern
Two pieces are required, one in each account:
// Account A — trust policy of RoleA
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::ACCOUNT_B:role/RoleB" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "unique-shared-secret" }
}
}
// Account B — permission policy attached to RoleB
{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::ACCOUNT_A:role/RoleA"
}
Without both halves the call fails. The ExternalId condition is non-negotiable for any third-party (SaaS) cross-account trust — AWS explicitly warns about confused-deputy risk in its docs.
GitHub Actions OIDC and IRSA
GitHub Actions OIDC lets a workflow assume an AWS role without storing long-lived keys as repo secrets. Set up the OIDC provider (token.actions.githubusercontent.com) in IAM once, then trust it with a condition on sub like repo:my-org/my-repo:ref:refs/heads/main. Wildcards are allowed but be deliberate — repo:my-org/*:* trusts every workflow in the org.
IRSA (IAM Roles for Service Accounts) is the same pattern for EKS — Kubernetes service accounts get IAM credentials via the cluster's OIDC provider. Both rely on sts:AssumeRoleWithWebIdentity.
Common pitfalls
- Forgetting
ExternalIdin a SaaS vendor trust — opens you to confused-deputy attacks. - Trusting
arn:aws:iam::ACCOUNT:rootwith no further conditions — this delegates ALL identity decisions to the other account. - Wildcards in the Principal field (
"Principal": "*") make the role assumable by anyone on the internet — only safe with strict conditions. - Service principals are case-sensitive:
Lambda.amazonaws.comis wrong,lambda.amazonaws.comis right. - Updating the trust policy of a role in use does not revoke active sessions — short-lived STS credentials keep working until their TTL expires.
FAQ
Trust policy vs permission policy? Trust policy says who can enter the role; permission policy says what they can do once inside. Every role has exactly one of the first and one or more of the second.
Is a trust policy mandatory for every role? Yes — without it, no one can assume the role and the role is useless. AWS rejects role creation if you do not supply a trust policy.
Can I list multiple Principals? Yes — Principal accepts a single value or an array: "Principal": {"Service": ["ec2.amazonaws.com", "lambda.amazonaws.com"]}. Useful for shared execution roles.
What is the ExternalId for? A shared secret between a SaaS vendor and you, passed in the AssumeRole call. Prevents an attacker from getting the vendor to operate on the wrong customer's account (confused deputy).
Why is GitHub OIDC better than IAM users for CI? No long-lived access keys to leak in repo secrets. Short-lived STS credentials minted per workflow run, scoped via the sub claim to a specific repo/branch.
Related Tools
Handwriting Generator
Convert typed text into an image with handwriting appearance. Useful for adding a personal touch to digital work.
Resume Generator
Fill a simple printable A4 CV from a form with personal data, education and experience.
Favicon Generator
Generate a favicon from text/emoji in all common sizes (16, 32, 48, 64, 192, 512). PNG download.