AWS IAM for Backend Workloads¶
A backend service often needs to read an object from S3, publish to SQS, write a metric, or retrieve a secret. The important question is not only which API the application calls. It is which workload identity AWS sees, how that identity receives credentials, and exactly which resources it is allowed to use.
This chapter focuses on workload-to-AWS authorization. It is different from authenticating a customer to your HTTP API: the application may already have authenticated a user, but AWS still needs to decide whether the running process may call an AWS service. The existing API authentication and authorization guide covers the user and service identity presented to your own API.
The default design for an AWS-hosted backend is an IAM role attached to the compute workload, with short-lived credentials delivered by the platform. Avoid embedding long-lived access keys in source code, container images, environment files, or deployment manifests.
1. Understand the two policies attached to a role¶
An IAM role is an identity that a trusted principal can assume. A role is not a password or a static access key. When a workload assumes it, AWS Security Token Service (STS) issues temporary credentials for a role session.
A role has two distinct policy concerns:
| Policy | Question it answers | Typical content |
|---|---|---|
| Trust policy | Who may assume this role? | AWS service principal, AWS account, or federated identity with conditions |
| Permissions policy | What may the assumed role do? | Actions, resources, and request conditions |
The trust policy is a resource-based policy attached to the role. The permissions policy is normally identity-based and attached to the role. A correct permissions policy does not help if the workload is not trusted to assume the role. A correct trust policy does not grant access to S3, SQS, or DynamoDB by itself.
A useful mental model is:
- The platform proves the workload's identity to AWS.
- The trust policy decides whether that principal may assume the role.
- STS issues a temporary role session.
- AWS evaluates the session's effective permissions for each API request.
- The SDK refreshes credentials through its configured provider when supported.
Keep these steps separate when troubleshooting. An error retrieving credentials is not the same as an authorization denial after credentials have been obtained.
2. Give each workload its own identity¶
Prefer a role per independently deployed service, worker, or security boundary. If an API, scheduled job, and report generator all use the same broad role, compromise of one component can expose the permissions of all three. Separate roles make access reviews, incident containment, and gradual permission reduction easier.
The delivery mechanism depends on where the process runs:
| Runtime | Workload identity mechanism | Important distinction |
|---|---|---|
| EC2 | IAM role associated through an instance profile | The instance profile makes the role available to software on the instance |
| ECS | Task role | Grants AWS permissions to application code inside the task |
| Lambda | Execution role | Grants the function's code permissions to call AWS services |
| EKS | EKS Pod Identity or IAM Roles for Service Accounts (IRSA) | Maps a Kubernetes service account to an IAM role |
For ECS, do not confuse the task role with the task execution role. The task role is used by application code—for example, to read an S3 object. The execution role is used by the ECS/Fargate agent for setup operations such as pulling an image, writing container logs, or retrieving configured secrets. Putting application permissions on the execution role grants them to the wrong actor.
For EKS, AWS currently documents two workload mechanisms. EKS Pod Identity is the recommended option for pods whenever possible; it uses the EKS service principal and role associations managed through EKS. IRSA uses the cluster's OIDC provider and a role trust policy tied to the service account. IRSA remains useful where Pod Identity is unsupported or where an existing OIDC-based setup is already established. Check the current compatibility requirements for the cluster, node type, and AWS SDK before choosing.
Do not let every pod or container inherit a broad node role. Workload-specific roles reduce blast radius, but they do not replace runtime isolation or metadata-service protections.
3. Write permissions for the actual operation¶
An identity policy should describe the smallest useful set of actions and resources. Start from a concrete application operation rather than attaching a broad managed policy and hoping to tighten it later.
For example, suppose an order API reads objects from one S3 bucket, but only beneath the orders prefix. The example policy separates object reads from bucket listing:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOrderObjects",
"Effect": "Allow",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::example-orders-prod/orders/*"
},
{
"Sid": "ListOnlyOrdersPrefix",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::example-orders-prod",
"Condition": {
"StringLike": {
"s3:prefix": ["orders/", "orders/*"]
}
}
}
]
}
The resource ARN for an object includes the object path; the bucket-level ListBucket action uses the bucket ARN. Applying an object ARN to ListBucket is a common policy mistake. The prefix condition limits which keys the role can enumerate.
The complete example is in orders-read-policy.json. The bucket name is illustrative and must be replaced with the real resource. If the application needs to write objects, delete objects, read object versions, or use a customer-managed KMS key, add only the corresponding actions and resources after confirming the operation's requirements.
A few rules help keep policies reviewable:
- Prefer explicit action names over service-wide wildcards such as s3:*.
- Scope resource ARNs wherever the service supports resource-level permissions.
- Use conditions for tenant, prefix, encryption, network, organization, or service context where those keys are supported by the action.
- Separate unrelated permissions into statements with clear Sid values.
- Do not assume every action supports a resource ARN. Some list or account-level actions legitimately require Resource set to *; compensate with supported conditions and a narrow action list.
- Treat a policy copied from a tutorial as a starting point, not a production grant.
4. Reason about effective permissions, not one JSON file¶
AWS authorization is not simply “the attached policy says Allow, therefore the request succeeds.” By default, a request is implicitly denied. An applicable explicit Deny overrides an Allow.
For same-account requests, identity-based and resource-based policies can both grant access, while explicit Deny overrides Allow. Permissions boundaries, session policies, SCPs, and RCPs can further restrict the result. Their interaction with resource policies depends on the principal and policy form, so do not apply one universal intersection formula.
Cross-account role access requires the caller to be allowed to assume the target role and the target trust policy to trust that caller. The assumed role then needs permission for the resource. Direct cross-account access also requires the resource policy and caller-side permissions required by that service.
When access is denied, inspect the complete path:
- Which principal made the request? Confirm the role session, not just the role name.
- Which identity policy grants the action and resource?
- Is there a resource policy, permissions boundary, session policy, SCP, RCP, or VPC endpoint policy?
- Does an explicit Deny or a failed Condition apply?
- Is the resource ARN correct for the action and Region?
Use the IAM policy simulator as a reasoning aid, then validate the actual service request in a non-production environment. A simulator result is not proof that the application's credentials, endpoint, resource policy, or runtime configuration are correct.
5. Restrict who can assume the role¶
The trust policy is a security boundary. Avoid a wildcard Principal with Allow unless public or unrestricted trust is genuinely intended. Trust only the required service, account, or federated principal, and add conditions that bind the trust to the expected source.
For an ECS task role, AWS documents a trust relationship using the ECS tasks service principal plus the source account and ECS ARN pattern. The ARN condition is scoped to the account and Region; AWS notes that specifying an individual ECS cluster in this SourceArn condition is not currently supported.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowTasksFromExpectedAccount",
"Effect": "Allow",
"Principal": {
"Service": "ecs-tasks.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn:aws:ecs:ap-south-1:111122223333:*"
},
"StringEquals": {
"aws:SourceAccount": "111122223333"
}
}
}
]
}
The example is also available as ecs-task-trust.json. Replace the Region and account ID. The source conditions help prevent a confused-deputy problem in which a trusted service is tricked into using its authority on behalf of an unintended customer or resource.
For third-party SaaS role assumption, use a provider-generated ExternalId unique to the customer context; it is not a password. For AWS service integrations, use the SourceArn, SourceAccount, or organization conditions documented for that service.
EKS Pod Identity uses the EKS service principal and required STS actions, including session tagging where configured. Do not copy an IRSA OIDC trust policy into a Pod Identity role.
6. Treat iam:PassRole as a deployment privilege¶
A deployment identity may need to configure an ECS task or Lambda function with a role. iam:PassRole authorizes that configuration step; it is not the workload's runtime role assumption. Because a service can exercise the passed role's permissions, restrict PassRole to approved role ARNs and the intended service with iam:PassedToService where supported.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PassOnlyOrdersTaskRoleToECS",
"Effect": "Allow",
"Action": ["iam:GetRole", "iam:PassRole"],
"Resource": "arn:aws:iam::111122223333:role/orders-api-task",
"Condition": {
"StringEquals": {
"iam:PassedToService": "ecs-tasks.amazonaws.com"
}
}
}
]
}
This deployment policy is separate from the application's runtime permissions. See deployer-passrole.json. Do not grant a CI role PassRole on every role in the account simply to make deployments easier.
PassRole is a permission, not a standalone API call, so it does not create its own CloudTrail event. To audit which role was passed, inspect the event that created or modified the receiving resource, such as the relevant function or task configuration operation.
7. Let the SDK obtain and refresh credentials¶
Use the SDK's default credential provider chain instead of reading access keys from application configuration. Provider order varies by SDK; once a valid source is found, the search stops. Standardized providers can refresh temporary credentials automatically when supported. Local development may use IAM Identity Center, while ECS, EC2, and EKS receive credentials through their runtime providers.
A common deployment failure is an old access key injected through an environment variable or shared credentials file. Because a valid earlier provider can win, the application may use that key instead of the task or instance role. Remove unintended static credentials and verify the principal from inside the actual process environment.
For a quick identity check, run the following in the same runtime context as the application:
The returned account and ARN help confirm which principal the CLI or SDK environment is using. This command identifies the caller; it does not prove that the caller has permission to perform a particular S3, SQS, or database operation.
8. Use federation for workloads outside AWS¶
Workloads outside AWS can still use temporary credentials. Prefer OIDC federation for supported CI/CD systems, IAM Roles Anywhere for X.509-based workloads, or STS role assumption from an existing trusted identity. Restrict the issuer, audience, subject, account, or certificate authority; a repository-wide OIDC subject may be too broad. For cross-account access, use a dedicated target-account role and keep role chains short and attributable.
9. Validate, observe, and reduce permissions over time¶
Policy review should be part of delivery, not a one-time console exercise.
- Validate identity and resource policies with IAM Access Analyzer.
- Use CloudTrail to inspect the principal, action, resource, and request context.
- Review unused roles and permissions before removing grants.
- Test with the real runtime identity in a non-production account; an administrator profile can hide missing task-role permissions.
- Separate AccessDenied errors from credential-provider, STS, and network failures.
Access Analyzer can generate a policy template from CloudTrail activity, which is useful for refining permissions. Do not treat that generated policy as a complete audit: AWS documents that S3 data events are not included in generated action-level activity and iam:PassRole is not tracked in CloudTrail for policy generation. Review the missing paths manually and test the resulting policy before deployment.
10. Common failure modes¶
- Shared role across services: One compromise exposes unrelated permissions; split roles by workload.
- Application permissions on the ECS execution role: Put application access on the task role, not the agent's execution role.
- Broad trust or wildcard resources: Restrict principals, actions, resources, and supported conditions.
- Static credentials shadow the role: Inspect the SDK provider chain and remove unintended keys.
- Overbroad PassRole: Restrict the role ARN and destination service.
- A simulated Allow still fails: Check the real principal, resource and endpoint policies, condition context, and service-specific rules.
11. Interview questions with reasoned answers¶
Q1. What is the difference between a trust policy and a permissions policy?
Trust controls who can assume the role; permissions control what the resulting session can do. Both are required.
Q2. Why should ECS use a task role instead of the EC2 instance role?
A task role gives application code a task-specific identity. The execution role is for ECS setup operations such as image pulls and logging. On EC2 launch types, protect instance metadata and account for co-located container isolation.
Q3. How do EKS Pod Identity and IRSA differ?
Pod Identity uses an EKS-managed association and service principal. IRSA uses the cluster OIDC provider and AssumeRoleWithWebIdentity. AWS recommends Pod Identity where supported; IRSA remains useful for compatible environments and established setups.
Q4. Does an Allow in a role policy guarantee access?
No. Explicit Deny, boundaries, session and organization policies, endpoint policies, and resource policies can change the result. Cross-account access also needs the correct trust.
Q5. Why is iam:PassRole sensitive?
It lets a principal configure a service with a role. A service can then exercise that role's permissions, so restrict the role ARN and destination service.
Q6. Why might ECS code use static keys instead of its task role?
A valid earlier provider can win in the SDK chain. Environment variables, a shared profile, or explicit configuration may shadow container credentials.
Q7. Are role sessions permanent?
No. STS credentials expire. Standard SDK providers refresh them when supported; custom providers must handle expiry and transient STS failures.
Q8. Can IAM Access Analyzer generate a complete least-privilege policy automatically?
It can generate a useful starting policy from observed CloudTrail activity, but observed activity is not the same as every required path. Some data events and PassRole usage are not represented in generated policies. Review the output, test infrequent workflows, and validate the policy before rollout.
Design checklist¶
- Give each independently deployed backend workload a distinct role.
- Keep trust policy and permissions policy responsibilities separate.
- Use temporary credentials from the compute platform or a federated identity provider.
- Scope actions, resources, and conditions to the actual workload operation.
- Restrict PassRole to approved roles and services.
- Check effective permissions across identity, resource, boundary, session, endpoint, and organization policies.
- Verify the caller identity from the running workload.
- Validate policies and test with the real runtime identity before production rollout.
- Review CloudTrail activity and unused permissions as the system evolves.
Further reading¶
- AWS IAM security best practices
- IAM roles and role concepts
- IAM policy evaluation logic
- AWS SDK standardized credential providers
- Amazon ECS task IAM roles
- Amazon ECS task execution role
- Granting AWS access to EKS workloads
- The confused deputy problem
- Granting iam:PassRole permissions
- IAM Access Analyzer policy validation
- IAM Access Analyzer policy generation