Request an IAM Role
How to add a new IAM role for a service or CI/CD workflow.
When You Need This
- Your service needs an AWS role to access resources (S3, DynamoDB, Lambda, etc.)
- Your CI/CD workflow needs a GitHub Actions OIDC role
- You need an App Runner access or instance role
Steps
1. Create a Feature Branch
git checkout master && git pull
git checkout -b feat/iam-your-service-role
2. Define the Role
Edit global/iam/roles.tf and add your role in the appropriate section:
Service role (Lambda, App Runner, etc.):
resource "aws_iam_role" "your_service_role" {
name = "your-service-role-name"
description = "Role for your service — brief purpose"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "lambda.amazonaws.com" # or the relevant service
}
Action = "sts:AssumeRole"
}
]
})
tags = {
Name = "your-service-role-name"
Service = "your-service"
Environment = "dev"
ManagedBy = "terraform"
}
}
3. Attach Policies
Edit global/iam/policies.tf to attach managed or custom policies:
resource "aws_iam_role_policy_attachment" "your_service_basic" {
role = aws_iam_role.your_service_role.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
4. Validate and Plan
task infra:validate
task infra:plan
5. Commit and Create PR
git add global/iam/roles.tf global/iam/policies.tf
git commit -m "feat(iam): add IAM role for your-service"
git push origin feat/iam-your-service-role
gh pr create --base master
Include in the PR description:
- What the role is for
- Which service or workflow will assume it
- What permissions it needs and why
6. Apply After Approval
CONFIRM=yes task infra:apply
GitHub Actions OIDC Roles
GitHub Actions OIDC roles follow a three-tier trust model defined in ADR-003.
Trust Tiers
| Tier | Subject Pattern | Use Case |
|---|---|---|
| CI (read) | repo:ontopix/*:* | Any branch — lint, test, build, read dependencies |
| Deploy (write) | repo:ontopix/*:ref:refs/heads/{master,pre,dev} | Deploy branches — push images, deploy code |
| Release (publish) | repo:ontopix/*:ref:refs/tags/* | Tags — publish packages, release artifacts |
Some roles combine tiers: e.g. ECR Push and CodeArtifact Publish are Deploy + Release (they trust both deploy branches and tags).
Where to Add the Role
| Target Service | Module | File |
|---|---|---|
| ECR | global/ecr/ | iam.tf |
| CodeArtifact | global/codeartifact/ | iam.tf |
| Other (S3, Lambda, CloudFront, etc.) | global/iam/ | github_oidc_roles.tf |
If the target service has its own module in this repo, the OIDC role belongs there. Otherwise, add it to global/iam/github_oidc_roles.tf.
Template: GitHub Actions OIDC Role
resource "aws_iam_role" "your_github_role" {
name = "GitHubActions-{Service}-{Access}Role"
description = "Brief description of what this role allows"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Federated = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:oidc-provider/token.actions.githubusercontent.com"
}
Action = "sts:AssumeRoleWithWebIdentity"
Condition = {
StringEquals = {
"token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
}
StringLike = {
# Choose the subject pattern matching your trust tier (see table above)
"token.actions.githubusercontent.com:sub" = "repo:ontopix/*:*"
}
}
}
]
})
tags = {
Name = "GitHubActions-{Service}-{Access}Role"
Environment = "global"
ManagedBy = "terraform"
Purpose = "github-actions-{tier}" # ci, deploy, release, or deploy-release
}
}
Important Constraints
- Ref-based subjects only: Do not use
environment:subjects in trust policies. GitHub Environments change the OIDC subject claim in ways that shift trust enforcement from AWS IAM to GitHub-side configuration. Ref-based subjects keep branch verification at the AWS level. - Org-wide by default: All roles use
repo:ontopix/*(wildcard). Per-repo scoping can be layered on for sensitive operations. - Export the ARN: Add the role ARN to the module's
outputs.tfand surface it inglobal/outputs.tfso consuming repos can reference it.
Lambda Deploy Convention
The GitHubActions-Lambda-DeployRole is designed to work with any project that follows the S3 artifact naming convention. You do not need to request a new IAM role for Lambda deployments.
What You Get Automatically
When your project creates an S3 bucket named {project}-{env}-lambda-artifacts[-suffix]:
- S3 access: Upload and read Lambda ZIPs under the
lambdas/key prefix - Lambda deploy: Update function code from S3, read code metadata
- Smoke tests: Invoke Lambda functions for post-deploy health checks
- SSM read: Read SSM parameters for configuration drift detection
S3 Bucket Naming Convention
{project}-{env}-lambda-artifacts[-optional-suffix]
Examples:
maxcolchon-dev-lambda-artifactsschemas-prod-lambda-artifacts-123456789012myproject-pre-lambda-artifacts
S3 Key Convention
lambdas/{component}/{version}-{sha}.zip
Example: lambdas/handler-intake/1.2.0-abc1234.zip
Setup Steps
- Define the S3 bucket in your project's
.infra/directory following the naming convention - Run
terraform applyto create the bucket (developer action, per P1) - Configure your
deploy.ymlworkflow to upload ZIPs and callaws lambda update-function-code - The
GitHubActions-Lambda-DeployRolealready has the necessary permissions — no infra PR needed
Naming Conventions
| Role Type | Pattern | Example |
|---|---|---|
| Service role | {service}-{purpose}-{env} | mcp-hello-world-apprunner-access-dev |
| GitHub OIDC | GitHubActions-{Service}-{Access}Role | GitHubActions-ECR-PullRole |
| Amplify | Amplify{Purpose} | AmplifyAdmin |
| CDK | cdk-hnb659fds-{purpose}-{account}-{region} | cdk-hnb659fds-cfn-exec-role-*-eu-central-1 |
Principles
- Code vs Infrastructure boundary: GHA roles update code in existing resources; infrastructure lifecycle (
terraform apply) is developer-only. See ADR-003 for details. - Least privilege: Request only the permissions your service actually needs
- Convention over configuration: Follow naming conventions (e.g., S3 bucket naming) to leverage existing roles instead of requesting new ones
- Descriptive names: Role names should clearly indicate their purpose
- Proper tagging: Include
Name,Service,Environment, andManagedBytags