How to create access for applications without IAM users

Topic: Accounts access

Summary

Grant applications access to AWS without IAM user access keys: use IAM roles for EC2, Lambda, ECS, and other services so workloads assume a role and get temporary credentials. Use this for all new and existing apps to avoid long-lived keys and meet least privilege.

Intent: How-to

Quick answer

  • Prefer IAM roles for AWS-hosted workloads: EC2 instance profile, Lambda execution role, ECS task role; the service assumes the role and receives temporary credentials with no keys on disk.
  • For external or on-premises apps, use IAM roles with OIDC/web identity or assume-role from a trusted identity; avoid creating IAM users and access keys for applications when roles are possible.
  • Attach only the policies the app needs to the role; audit and rotate nothing (roles use short-lived credentials); document the role per app for operations.

Prerequisites

Steps

  1. Choose the right role type for the workload

    EC2/on-instance app → instance profile role; Lambda → execution role; ECS → task role; external CI (e.g. GitHub Actions) → OIDC role with trust for the IdP. Avoid IAM user keys for app identity.

  2. Create the role and trust policy

    Create a role with trust policy for the service (ec2.amazonaws.com, lambda.amazonaws.com) or for the OIDC provider; restrict trust (e.g. source ARN for Lambda, repo for GitHub OIDC) for least privilege.

  3. Attach least-privilege permissions

    Attach managed or custom policies that grant only the actions and resources the app needs (e.g. S3 bucket, Secrets Manager secret); test the app to confirm it can perform required operations.

  4. Assign the role to the workload

    Attach the instance profile to EC2, set the execution role on Lambda, set the task role on ECS, or configure OIDC federation for external CI; verify the app runs and get-caller-identity shows the role.

Summary

You will create access for applications without IAM users by using IAM roles for EC2, Lambda, ECS, and other services (or OIDC for external CI). The app assumes the role and gets temporary credentials; no long-lived access keys are stored. Use this for all new apps and when migrating away from user keys.

Prerequisites

Steps

Step 1: Choose the right role type for the workload

  • EC2, ECS EC2 launch type: Instance profile role; the instance or task gets credentials via the metadata service.
  • Lambda: Execution role; Lambda assumes it on each invocation.
  • ECS Fargate: Task execution role and optionally task role; same idea as Lambda/EC2.
  • External (e.g. GitHub Actions, GitLab CI): IAM role with trust for OIDC (e.g. token.actions.githubusercontent.com); the external IdP issues a token and the app exchanges it for AWS credentials via AssumeRoleWithWebIdentity. No IAM user or access key.

Avoid creating an IAM user and access key for the application when a role can be used.

Step 2: Create the role and trust policy

Use the appropriate trust policy:

  • EC2: "Principal": {"Service": "ec2.amazonaws.com"}
  • Lambda: "Principal": {"Service": "lambda.amazonaws.com"}; optionally add Condition StringEquals lambda:FunctionArn to restrict to one function.
  • GitHub OIDC: Trust token.actions.githubusercontent.com with Condition on repository and (optionally) branch. See AWS and GitHub docs for the exact trust and claim format.

Create the role (CLI or console) and attach no permissions until Step 3.

Step 3: Attach least-privilege permissions

Attach policies that grant only what the app needs. Examples: S3 read for one bucket, Secrets Manager get for one secret, DynamoDB read/write for one table. Prefer custom policies with specific Resource ARNs over broad managed policies. Test the application in a dev environment to confirm it can perform required operations and is denied for others.

Step 4: Assign the role to the workload

  • EC2: Attach the instance profile (same name as role when created via console) to the instance.
  • Lambda: Set the function’s execution role to this role.
  • ECS: Set the task role (and execution role if needed) in the task definition.
  • GitHub Actions: Configure the job to use OIDC and assume the role (e.g. aws-actions/configure-aws-credentials with role-to-assume).

From inside the workload, run aws sts get-caller-identity (or equivalent) to confirm the returned ARN is the role.

Verification

  • The application runs and can call the allowed AWS APIs; no access keys are configured in the app or environment.
  • get-caller-identity from the workload returns the role ARN.
  • No IAM user exists for this application; the role is documented (name, purpose, attached policies).

Troubleshooting

App reports no credentials — Ensure the role is attached (instance profile on EC2, execution role on Lambda). For EC2, check IMDS and that the app uses the default credential chain. Restart the app after attaching the role.

Access denied for an API — Add the required action and resource to the role’s policies. Use the policy simulator with the role ARN and the failing action. Check for SCP or permission boundaries.

External OIDC assume fails — Verify the IdP’s issuer and audience in the trust policy; ensure the token is passed correctly and the condition on sub/repo matches the token claims.

Next steps

Continue to