This page is generated from skills/ecs-security/references/identity-and-access.md. Edit the source, not this page.
Layer 2 — Identity & Access on ECS
This is the highest-value layer for ECS because the #1 recurring hard question in the field is a role-trust misconfiguration: "ECS was unable to assume the role." Two concerns compound: which role does what, and how to make each trust policy least-privilege and confused-deputy-safe.
The four ECS roles — get them distinct
ECS uses several IAM roles for different jobs. Conflating them is the classic misconfiguration. Reference: Best practices for IAM roles in Amazon ECS.
| Role | Who uses it | For what | When required |
|---|---|---|---|
| Task role | Your application code inside the container | Calls to other AWS services (S3, DynamoDB, …) at runtime | When the app accesses AWS services |
| Task execution role | The ECS/Fargate agent, not your code | Pull images from ECR, write logs (awslogs), fetch Secrets Manager/SSM secrets at launch, Runtime Monitoring, private-registry auth | Fargate, ECS Managed Instances, or external instances for ECR-private + awslogs; Fargate or EC2 for secrets/private-registry/Runtime Monitoring (verified — task execution role) |
| Container instance role | The EC2 instance (EC2 launch type) | Register the instance with the cluster, agent → ECS API | ECS on EC2 / external instances |
| Infrastructure role | ECS itself | Manage EBS volume attach, Service Connect TLS, VPC Lattice target groups on your behalf | When using those features |
The single most important distinction (verified): the execution role grants the agent permission to prepare and launch the task (pull image, write logs, retrieve secrets). The task role vends temporary credentials to your application code at runtime via the container credentials endpoint (
AWS_CONTAINER_CREDENTIALS_RELATIVE_URI→169.254.170.2). Putting your app's S3 permissions on the execution role, or putting ECR/secrets-retrieval permissions only on the task role, is the classic error. Sources: task execution role · task role.
- Use the AWS managed
AmazonECSTaskExecutionRolePolicyas the execution-role baseline, then add inline permissions for the specific secrets ARNs the task reads. Do not overload it. - One task role per task definition/service, least-privileged — AWS explicitly recommends a distinct role per task definition with only the permissions that task needs, rather than a shared role.
- Task-role credentials are, by default, valid for six hours and auto-rotated by the agent; app code doesn't manage renewal (modern SDKs fetch from the credentials endpoint automatically). Verified 2026-07-09: "By default, credentials assigned to tasks using task roles are valid for six hours" (Best practices for IAM roles in Amazon ECS).
Trust policy — the ecs-tasks.amazonaws.com principal
Both the task role and the execution role must trust the ECS tasks service. The minimal trust policy is below — not for production as-is: it lacks the confused-deputy conditions added in the next section.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}]
}
Confused-deputy protection (add aws:SourceArn + aws:SourceAccount)
Because ecs-tasks.amazonaws.com is a shared AWS service principal, harden the trust policy against the confused-deputy problem — so the ECS service can only assume the role on behalf of your account's tasks, not another customer's. Scope with aws:SourceAccount (your account) and aws:SourceArn. The exact form AWS documents (verified 2026-07-09 against ECS task IAM role) uses an all-clusters wildcard arn:aws:ecs:region:account:*:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "aws:SourceAccount": "111122223333" },
"ArnLike": { "aws:SourceArn": "arn:aws:ecs:region:111122223333:*" }
}
}]
}
Gotcha (corrected): you cannot scope this
aws:SourceArnto a specific cluster or task-family. AWS states verbatim: "Using theaws:SourceArncondition key to specify a specific cluster is not currently supported, you should use the wildcard to specify all clusters" — so the account-scoped...:account:*above is the tightest supported form. By inspection of the ARN format (an inference, not an AWS-documented statement): a task ARN isarn:aws:ecs:region:account:task/cluster-name/task-idand never contains the family, so a task-family SourceArn has nothing to match against. Attempting a narrower pattern is the classic self-inflicted "unable to assume the role." Source: ECS task IAM role · AWS confused-deputy prevention.
iam:PassRole — scope it, never *
Whoever registers a task definition or creates a service (a CI/CD pipeline, CodeDeploy, EventBridge scheduler, a developer) must have iam:PassRole for the task role and execution role being attached — this is how AWS prevents privilege escalation via role attachment. Scope it to the exact role ARNs:
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": [
"arn:aws:iam::111122223333:role/myAppTaskRole",
"arn:aws:iam::111122223333:role/ecsTaskExecutionRole"
],
"Condition": { "StringEquals": { "iam:PassedToService": "ecs-tasks.amazonaws.com" } }
}
Never grant iam:PassRole on Resource: "*" — that lets the principal attach any role (including an admin role) to a task; over-broad iam:PassRole is a well-known container privilege-escalation path. (Separately, AWS has deprecated/phased out several broad ECS managed policies such as AmazonEC2ContainerServiceFullAccess and AmazonEC2ContainerServiceRole in favor of scoped policies and service-linked roles — stated here as a deprecation, not a documented causal link. See Phased-out AWS managed IAM policies for ECS.) CodeDeploy, EventBridge, and CI runners each need their own scoped iam:PassRole — see CodeDeploy IAM role, EventBridge IAM role, infrastructure role pass permission.
Diagnosing "ECS was unable to assume the role"
This is the recurring firefight. Work the checklist in order (per re:Post: ECS unable to assume role):
- Does the role exist?
aws iam get-role --role-name <role>— a deleted/renamed/typo'd role ARN in the task definition is the most common cause. - Trust policy correct? It must allow
sts:AssumeRoleforPrincipal.Service = ecs-tasks.amazonaws.com. A trust policy pointing atec2.amazonaws.com(copied from an instance profile) is a frequent mistake. - Confused-deputy condition too tight? An
aws:SourceArn/aws:SourceAccountcondition that doesn't match the actual account will deny the assume. Widenaws:SourceArnto the documented all-clusters wildcardarn:aws:ecs:region:account:*(cluster/task-family scoping is unsupported — see above) and confirmaws:SourceAccountis the launching account. - Right role in the right field? Confirm the execution role is in
executionRoleArnand the task role intaskRoleArn— swapping them causes launch-time failures (agent can't pull/log) or runtime failures (app can't call AWS). iam:PassRolepresent for the principal creating the service/registering the task def.- Self-assume edge case: if a task's role must assume itself, the trust policy must explicitly allow that (per Updating a role trust policy).
ECS Exec governance (the biggest under-controlled surface)
ECS Exec (aws ecs execute-command) opens an interactive shell into a running container. It is invaluable for break-glass debugging and a serious lateral-movement risk if ungoverned — and note two verified facts (2026-07-09, ECS Exec): Exec commands always run as root ("these commands are run as the root user … even when you specify a user ID for the container"), and readonlyRootFilesystem: true is not supported with Exec (the SSM agent must write to the container FS). Govern it explicitly:
- Restrict who can Exec, and into what — scope
ecs:ExecuteCommandin the caller's IAM policy with condition keys:ecs:cluster(which clusters),ecs:container-name(which containers), and resource tags on the cluster/task. AWS shows these exact patterns under Using IAM policies to limit access to ECS Exec. Also consider limiting the underlyingssm:StartSession. - Log every session — set the cluster
configuration.executeCommandConfigurationto log sessions to CloudWatch Logs and/or S3 (logging: OVERRIDE), and encrypt the session with a KMS key (kmsKeyId). The KMS permissions are split: the task role needskms:Decrypton the key, and the caller (the user/role runningexecute-command) needskms:GenerateDataKeyon it — both, or the session fails (verified 2026-07-10 — ECS Exec). When using a KMS key you must also add a KMS interface VPC endpoint on private networks. - Audit —
ExecuteCommandis a CloudTrail event; alert on it. Session command/output logs give you the in-container audit trail. - Disable in production — the task must opt in via
enableExecuteCommand; leave it off for prod services unless a break-glass workflow is active, and pair the task-role SSM permissions — the four documented actionsssmmessages:CreateControlChannel,ssmmessages:CreateDataChannel,ssmmessages:OpenControlChannel,ssmmessages:OpenDataChannel, not assmmessages:*wildcard (ECS Exec permissions) — with the IAM restrictions above.
Operator-side IAM (cluster administration, not just workload roles)
Layers above cover the workload roles; the operators who manage ECS need least-privilege too (per ECS security IAM best practices and the ECS security best-practices guide):
- Treat the cluster as an administrative boundary and split operator personas — e.g. an
AdminRolethat canCreateCluster/DeleteCluster/UpdateServicevs aDeveloperRolescoped to deploy within named clusters. - ABAC — tag clusters/services and gate operator actions on matching principal/resource tags to scale least-privilege without per-resource policies.
- MFA conditions (
aws:MultiFactorAuthPresent) on destructive ECS actions, and scopeiam:PassRole(above) so operators can only pass the intended task/execution/infrastructure roles.
Cross-account access
A task role can assume a role in another account for cross-account resource access — the target account's role trusts the task role's ARN, and the task role holds sts:AssumeRole for it. Add confused-deputy conditions on the target trust policy too. For CloudTrail auditability, task credentials carry a taskArn session context so you can trace which task made a call (task role auditability).
Shared responsibility (Layer 2)
| AWS manages | Customer manages |
|---|---|
The ecs-tasks.amazonaws.com assume plane; credential vending to the container endpoint; default six-hour auto-rotation; CloudTrail recording; ECS Exec session transport (SSM) | Role split (task vs execution vs instance vs infra); least-privilege policies; trust-policy correctness + confused-deputy conditions; scoped iam:PassRole; cross-account trust; ECS Exec IAM restrictions + session logging/encryption; operator-role least-privilege |
Sources
- Best practices for IAM roles in Amazon ECS · ECS task IAM role · ECS task execution IAM role
- re:Post — "ECS was unable to assume the role" · AWS confused-deputy prevention · Updating a role trust policy
- CodeDeploy IAM role · EventBridge IAM role · Infrastructure role pass permission
- Monitor Amazon ECS containers with ECS Exec · Phased-out AWS managed IAM policies for ECS