-
-
Notifications
You must be signed in to change notification settings - Fork 17
Container Permissions
Your project's container runs on the incubator as an ECS task. When your application code calls an AWS API — reading a file from S3, sending a message to a queue, looking up a user in Cognito — it authenticates as an IAM role that the incubator creates for it.
This page explains which role that is, what it can already do, and how to get it a permission it does not have yet.
Every container gets two IAM roles, and they are not interchangeable.
| Task role | Execution role | |
|---|---|---|
| Named | ecs-container-<project>-<app>-<env> |
ecs-execution-<project>-<app>-<env> |
| Used by | your application code, at runtime | the ECS agent, before your code starts |
| Does | whatever your application asks AWS to do | pulls the image, creates the log stream, reads startup secrets |
| You want | this one | not this one |
Application permissions go on the task role. If your code calls an AWS API, it is the task role that has to allow it. The execution role is infrastructure plumbing: it exists so ECS can start your container at all, it is scoped to your project's ECR repository, log group and SSM parameters, and it is not an extension point for application code.
A name that will mislead you. There is a role in this account called
incubator-prod-ecs-task-role. Despite the name it is an execution role, not a task role — it is the shared, pre-2026 role that every container used before each got its own. No running container uses it any more. It is kept only until the module'suse_own_execution_roleescape hatch is removed, and both will be deleted together. If you are reading role names in the IAM console, the real task roles are theecs-container-*ones.
Before asking for a permission, check whether you have it. Every container's task role is granted these, with no request and no configuration:
-
aws ecs execute-command— an interactive shell into a running task, via the fourssmmessagesactions. -
S3 —
ListBucket,GetObject,PutObjectandDeleteObjecton any bucket tagged with your project. See the walkthrough below; there is one setup step. -
Cognito —
AdminGetUser,AdminCreateUser,AdminAddUserToGroupandAdminDeleteUseron any user pool tagged with your project.
All three are scoped by the project tag, so each project reaches its own resources and no
others. You do not need to ask for any of them.
Those four Admin* operations are the only Cognito calls that need IAM permission at all.
The rest of the sign-in surface — SignUp, ConfirmSignUp, ResendConfirmationCode,
InitiateAuth, RespondToAuthChallenge, GetUser, GlobalSignOut, ForgotPassword,
ConfirmForgotPassword — are unauthenticated APIs that authorize against the end user's own
credentials. They work with no role permissions and cannot be restricted by adding any. If
one of those is failing, the cause is somewhere other than IAM.
This is the most common request, and it needs no policy change — the grant above already
covers it. What it needs is for the bucket to be tagged and opted in. Three things, all in
your project's directory under terraform/projects/<project>/:
resource "aws_s3_bucket" "uploads" {
bucket = "hfla-myproject-uploads"
tags = {
project = local.project_name
}
}
resource "aws_s3_bucket_abac" "uploads" {
bucket = aws_s3_bucket.uploads.id
abac_status {
status = "Enabled"
}
}- The bucket.
-
The
projecttag, whose value is the HfLA project name — never an application or repository name. The permitted values are fixed; see the decision record linked at the bottom of this page. aws_s3_bucket_abacwith statusEnabled.
Step 3 is not optional, and skipping it fails silently. S3 does not evaluate tag conditions against a bucket that has not opted in to attribute-based access control. Leave it out and the policy still looks correct, the tag is still there, and your container gets
AccessDeniedwith nothing to explain why.
Enabling ABAC also changes how that bucket's tags are managed — PutBucketTagging gives way
to TagResource. Terraform handles this itself and the CI roles already hold the
permissions, so there is nothing for you to do about it.
If your application needs a service not in the list above, that is a real request and the
answer is a change to the shared container module.
The task role's policy lives in one place —
terraform/modules/container/main.tf,
in aws_iam_policy.container_policy — and it is shared by every container on the incubator.
A new permission is a new statement there, scoped by the project tag so that adding it for
your project does not hand the same access to everyone else's.
A statement granting a container access to its own SQS queues looks like this:
{
Sid = "ProjectQueueAccess"
Effect = "Allow"
Action = [
"sqs:SendMessage",
"sqs:ReceiveMessage",
"sqs:DeleteMessage",
]
Resource = "arn:aws:sqs:us-west-2:035866691871:*"
Condition = {
StringEquals = { "aws:ResourceTag/project" = var.project_name }
}
}The shape is always the same: a wildcard resource narrowed by the project tag, rather than a list of named resources. That is deliberate — it means a queue you create next month is covered the day you tag it, with no further policy change.
-
Not every AWS service supports this. A service has to offer both resource-level
permissions and the
aws:ResourceTagcondition key. Most do; some do not, and some support them on only a subset of their actions. The authoritative source is the AWS Service Authorization Reference — find your service, find the action, and check its "Resource types" and "Condition keys" columns before assuming. -
Some actions cannot be scoped at all.
ecr:GetAuthorizationTokenis the example we already live with: it is account-level, supports no resource-level permissions and no conditions, and so sits atResource: "*"in the execution role with a comment saying why. If the action you need turns out to be one of these, say so in your request — the decision then becomes whether the unscoped grant is acceptable, which is a conversation rather than a policy edit.
Open an issue on hackforla/incubator using Blank Issue Form with No Dependency.
The template picker on this repository is cluttered with templates inherited from the Hack for LA website repo — accessibility audits, team rosters, project logos — none of which apply here. There is also a
Blank issueentry that looks right and is a superseded version of the form. Pick Blank Issue Form with No Dependency; ignore the rest. Clearing them out is tracked as #146.
Include:
- Which project and which container — project name, application type and environment, which together give the role name.
- Which AWS service and which actions, as specific as you can be. "Read and write objects in our own bucket" is enough to start from; a blanket "S3 access" is not.
- What the application does with it, in a sentence. This is what makes it possible to tell whether a narrower grant would do.
- Whether the resources are tagged with your project, or need to be created.
- Container module documentation — the generated reference for the module, including its inputs, outputs and the full S3 and Cognito notes summarised above.
-
DR-Machine-to-machine-IAM-scoping
— the decision record behind all of this: what the
projecttag means, which resource types carry it, the permitted values, and the exceptions. - AWS Resources — where to find the generated Terraform documentation for everything the incubator runs.