How to Design a Least-Privilege OIDC Role for CI
A keyless role is only as safe as its trust scope and permission policy, so pin both to exactly what the job needs.
Least privilege for OIDC has two halves: who can assume the role (trust policy, scoped by sub) and what the role can do (permission policy, scoped to specific actions and resources).
Principles
- One role per pipeline or environment, not a single shared admin role.
- Trust policy pins
subto the repository and, for production, an exact ref or environment. - Permission policy lists explicit actions and resource ARNs, not wildcards.
- Keep session duration short so a leaked session expires quickly.
Scoped permission policy
Trust policy (JSON)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject", "s3:GetObject"],
"Resource": "arn:aws:s3:::my-app-artifacts/*"
},
{
"Effect": "Allow",
"Action": "ecr:GetAuthorizationToken",
"Resource": "*"
}
]
}Gotchas
- A broad
sublikerepo:org/repo:*plus admin permissions makes any branch admin; avoid it. - Prefer separate deploy and read-only roles over one role with both.
- Review CloudTrail for the
role-session-nameto confirm only intended runs assume the role.
Related guides
How to Scope an AWS OIDC Role to a Branch or EnvironmentRestrict which GitHub Actions runs can assume an AWS role by matching the sub claim on a branch, tag, pull re…
How to Use a Different OIDC Role Per Environment in GitHub ActionsAssume a different AWS role per deployment environment in GitHub Actions by selecting the role ARN from the e…