Skip to content

IAM roles

2 min read · main part only; folded sections are optional

After this you can answer: What is the difference between a task role and a task execution role?

flowchart TB
ECS["ECS agent"] -->|"uses"| EX["api execution role"]
EX --> E1["pull image; send logs"]
EX --> E2["read 3 secrets"]
CODE["api container"] -->|"uses credentials for"| TR["api task role"]
TR --> T1["SNS publish; DynamoDB Query"]
TR --> T2["S3 prefixes; ECS Exec channels"]
  1. Trust policy: who may assume the role. Both api roles trust only ecs-tasks.amazonaws.com, and only when aws:SourceAccount is our account. That stops another account’s ECS from using them.
  2. Permission policy: what the role may do. Each statement names actions and resources, for example sns:Publish on one topic ARN.
  3. Assuming gives short-lived credentials. ECS provides task-role credentials through a container endpoint; the SDK finds them. Execution-role credentials stay with the agent.
  4. Two roles, separate permissions. The execution role supports startup and logs. The task role permits app AWS calls. It cannot call Secrets Manager, but the code can read injected secret values.

All three roles are made in infra/src/compute/iam.ts → createIamRoles and infra/src/compute/web-iam.ts → createWebIamRole:

RoleUsed byMay do
minitickets-dev-api-execution-roleECS, api and migration tasksECR pull, log writes, read 3 secrets with entry on; 2 otherwise
minitickets-dev-api-task-rolethe api codepublish to booking-events, query the tickets table, some S3 prefixes, ECS Exec
minitickets-dev-web-execution-roleECS, web tasksECR pull, log writes, read only the revalidate secret

The web task has no task role: the Next.js code calls no AWS API.

The task role’s exact statements, generated from the code:

Who may assume API task role: API task definition (Principal ecs-tasks.amazonaws.com; condition aws:SourceAccount = our account (stops the confused deputy problem))

What it may do:

OnActionsPolicyWhy only this
Booking-events SNS topicsns:Publishidentity send-jobs createIamRolesThe outbox relay publishes booking events. The api cannot read either queue: SNS delivers to them with the queues' own policies.
Tickets tabledynamodb:Queryidentity ReadTickets createIamRolesMy tickets is one Query on the userId partition. No Scan, and no writes: only the tickets Lambda writes tickets.
Ticket assets buckettickets/* onlys3:GetObjectidentity ReadTicketPngs createIamRolesA presigned ticket GET works with the signer's permissions, so the api role must be allowed to read the ticket PNG it signs.
Ticket assets bucketposters/* and poster-uploads/* onlys3:GetObjectidentity ReadPosters createIamRolesThe api reads staged uploads to check them before publishing, and reads poster objects. This separate statement grants no ticket writes.
Ticket assets bucketposters/* and poster-uploads/* onlys3:PutObjectif s3:x-amz-server-side-encryption = AES256identity WritePosters createIamRolesThe browser's presigned poster PUT and the copy to posters/ run with this role. It can never overwrite tickets/.
API ECS serviceResource * (these actions cannot be limited to one resource)ssmmessages:CreateControlChannelssmmessages:CreateDataChannelssmmessages:OpenControlChannelssmmessages:OpenDataChannelidentity ecs-exec createIamRolesECS Exec opens a debug shell inside the api task, which can reach RDS and Valkey from inside the VPC.
See the task role on the map →

IAM → Roles → minitickets-dev-api-task-role → Permissions tab, then Trust relationships.

SymptomCauseWhere to look
Tasks stop: AccessDeniedException on GetSecretValueExecution role does not list that secretStopped reason; read-secrets policy
Bookings work, no email; “outbox delivery failed”Task role lost sns:Publishsend-jobs policy
First deploy of a new role fails once, retry worksIAM is eventually consistentCD log at that time
Why does the web task have its own execution role?

If it shared the api’s role, one wrong line in the web task definition could hand it the database password. Its own role may read only the revalidate secret, whatever the task definition asks for.

What is the confused deputy problem?

A trusted service is tricked into using its power for the wrong customer. The aws:SourceAccount condition makes ECS assume our roles only when it acts for our account.

What is the trade-off of AmazonECSTaskExecutionRolePolicy?

Its ECR pull and log-write actions have Resource *, not our repository or log-group ARNs. Other authorization rules still apply. It is maintained by AWS, but wider than our needs; see the managed policy JSON.

Advanced: Every statement of the execution role

The execution role also sends logs after startup; its credentials are not exposed to containers. ECS supplies separate task-role credentials through its container endpoint, without the app calling STS. See AWS execution roles and task roles.

Who may assume API task execution role: API task definition, Migration task definition (Principal ecs-tasks.amazonaws.com; condition aws:SourceAccount = our account (stops the confused deputy problem))

What it may do:

OnActionsPolicyWhy only this
ECR repositories, CloudWatch log groupsResource *; one AWS managed statement covers image pulls and log writesecr:GetAuthorizationTokenecr:BatchCheckLayerAvailabilityecr:GetDownloadUrlForLayerecr:BatchGetImagelogs:CreateLogStreamlogs:PutLogEventsidentity AmazonECSTaskExecutionRolePolicy (AWS managed, Resource *) createIamRolesECS pulls the image before startup, then the awslogs driver writes the container's output. The AWS managed statement applies to all repositories and log groups, not only ours.
RDS master password secret, Redis URL secret, Revalidate secretexactly these secret ARNs, never *; the revalidate secret only with entry onsecretsmanager:GetSecretValueidentity read-secrets createIamRolesECS puts DB_USER, DB_PASSWORD, REDIS_URL and REVALIDATE_SECRET into the container at start. No kms:Decrypt is needed: the secrets use the AWS managed key aws/secretsmanager.

Answer these without scrolling up. If one feels hard, reread that section.

  1. What do a trust policy and a permission policy each say?
  2. Which role pulls the image, and when?
  3. Why can the api read its injected database password, but not call Secrets Manager?
  4. What does the aws:SourceAccount condition protect against?
  5. Why does the web task have no task role?

Redraw it from memory: ECS using the execution role (image, secrets, logs) and the container using task-role credentials (SNS, DynamoDB, S3, ECS Exec)

Try this: on the map, select the API task role and open the Permissions tab: what can it touch, and what can it NOT touch?

Read this book online: https://book.theversehub.in/