Skip to main content

Identity

Identity answers two questions for every AWS call: who is asking (a person or code), and what are they allowed to do (policies).

Decision chart​

Start at the top and answer each question. Click a box to jump to its setup steps.

AWS recommends that people use federation and temporary credentials, and that code use roles. Long-term keys for an IAM user are the last resort, for the few cases that cannot use either. Whichever identity you pick, its permissions come from policies.

Compare the options​

OptionYou manageScalesCost shapeAWSAzureGCP
Person, single sign-onUsers and groups (or an external identity provider), permission sets, and which accounts each group can useMany people across many accounts from one placeNo charge for the service itself; AWS KMS charges apply to a multi-Region instanceIAM Identity CenterMicrosoft Entra IDIAM principals
Person or tool, long-term loginEach user's password, MFA device and access keys, plus rotating the keysUp to 5,000 users per account; every account needs its own usersIAM is offered at no additional chargeIAM usersEntra ID usersIAM principals
Code, temporary credentialsThe role's trust policy and its permissions; there is no password or key to storeA role can be used by any number of sessions; 1,000 roles per account by defaultIAM is offered at no additional chargeIAM rolesManaged identityService account
PermissionsThe policy documents: actions, resources and conditions, kept to least privilege1,500 customer managed policies per account by default; 20 managed policies per role by defaultIAM is offered at no additional chargeIAM policiesRBAC role assignmentsIAM roles bound on a resource

Set it up on AWS​

IAM Identity Center​

  • What it is: the AWS recommendation for people. You manage users once in IAM Identity Center (or connect your own identity provider), and they sign in to the AWS access portal and get temporary credentials for the accounts and roles you assign.
  • Enable it: open the IAM Identity Center console and choose Enable. The recommended organization instance needs AWS Organizations; from a standalone account, the console offers to create an organization with your account as its management account. An account instance (standalone account or a member account) only covers application assignments within that account.
  • Pick the Region carefully: your IAM Identity Center data is stored in the one Region where you enable it (its primary Region). To change that Region later you must delete the instance and create a new one in the other Region. You can instead replicate the instance to additional Regions (verify the steps in the IAM Identity Center User Guide, "IAM Identity Center Region data storage and operations").
  • Next steps: confirm the identity source (the built-in directory or an external provider), then create users or groups, create permission sets, and assign groups to accounts. This page does not cover those console screens step by step (verify them in the IAM Identity Center User Guide).
  • Use it from the CLI: run aws configure sso then aws sso login, as in Set up the AWS CLI.
  • Free tier note: creating an AWS organization from a free-tier account upgrades it to a paid plan with pay-as-you-go pricing, and its free tier credits expire immediately.

IAM users​

  1. Open the IAM console, choose Users, then Create user, and enter a user name.

  2. Decide whether the user needs the AWS Management Console. If so, create a password and select User must create a new password at next sign-in (recommended). Create only the credentials the user needs: a user who only uses the console needs no access keys.

  3. Set permissions. Put the user in a group and attach policies to the group instead of to each user. See IAM policies.

  4. Choose Create user, then open the user and use the Security credentials tab to add an MFA device.

    caution

    If you keep a user at all, require MFA for it. AWS recommends phishing-resistant MFA such as passkeys and security keys where possible.

  5. Only if code outside AWS must call AWS and cannot use a role: on the Security credentials tab, in Access keys, choose Create access key. On the Access key best practices and alternatives page, choose Other, then Next, then Create access key. Save the secret access key now.

    caution

    Long-lived access keys leak; prefer SSO / OIDC federation and short-lived credentials. The secret access key can be retrieved only when you create it, and a user can have at most two access keys.

  6. Rotate keys without downtime: create the second key, update every application to use it, check the Last used column of the old key, deactivate the old key, confirm nothing broke, and only then delete it.

    caution

    Do not delete the old key straight away, even if it shows no recent use. Deactivate it first: an application that still uses it stops working, and you can reactivate the key while you fix that application.

IAM roles​

  1. Open the IAM console, choose Roles, then Create role.

  2. For Trusted entity type, choose AWS service. For Service or use case, choose the service that will use the role (EC2 for a server, Lambda for a function), then choose Next. The use case sets the trust policy for you.

  3. For Permissions policies, select what the code needs. For an EC2 instance you reach with Session Manager, AmazonSSMManagedInstanceCore. For a Lambda function, AWSLambdaBasicExecutionRole lets it write logs to CloudWatch Logs.

    caution

    Start small and add permissions for what the code actually calls. Attach only the policies the workload needs, not an administrator policy. See IAM policies.

  4. Enter a Role name, then choose Create role.

    caution

    You cannot rename a role after it is created, because other things may refer to it by name. Role names must be unique in the account and are not distinguished by case.

  5. Use the role:

    • EC2: when you launch an instance, set IAM instance profile under Advanced details. See EC2.
    • Lambda: the console creates a default execution role (with AWSLambdaBasicExecutionRole) when you create a function. To use a role you made, choose Use another role in the Permissions section. See Lambda.
    caution

    When you create a role for EC2 in the console, it also creates an instance profile with the same name, and the EC2 launch screen lists instance profile names. A role made with the CLI or Terraform has no instance profile until you create one.

  6. To change the permissions of a running EC2 instance, replace its instance profile. Removing a role from an instance profile can take up to an hour to take effect.

Which role for which service​

  • EC2: an instance profile carries the role. The code on the instance (through the AWS SDK) gets its temporary credentials without any key on disk.
  • Lambda: the execution role. Lambda assumes it for you when the function is invoked, so avoid calling sts:AssumeRole yourself in the function. The trust policy must name lambda.amazonaws.com.
  • ECS on Fargate: two roles, not one. The task execution role (managed policy AmazonECSTaskExecutionRolePolicy) is used by the ECS and Fargate agents to pull the image from ECR and send logs to CloudWatch Logs; your containers cannot use it. The task role is what your application code uses to call AWS services. Both trust ecs-tasks.amazonaws.com. See ECS on Fargate.
  • Outside AWS: a role can also be assumed with a web identity token, a SAML assertion or an X.509 certificate (IAM Roles Anywhere). See the next section for GitHub Actions.

CI deploy without keys​

This is the AWS side of "GitHub OIDC -> IAM role" in the Common patterns table: a GitHub Actions workflow trades its OIDC token for temporary AWS credentials, so no access key is stored in GitHub.

  1. Create an IAM OIDC identity provider with the URL https://token.actions.githubusercontent.com and the audience sts.amazonaws.com.
  2. Create a role whose trust policy lets that provider assume it, limited to your repository.
  3. Let the workflow request the token and assume the role.
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com

The role's trust policy (create it like any role, with aws iam create-role --assume-role-policy-document file://github-trust.json):

github-trust.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:octo-org/octo-repo:ref:refs/heads/main"
}
}
}
]
}

The same thing in Terraform:

resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
}

data "aws_iam_policy_document" "github_trust" {
statement {
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.github.arn]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:octo-org/octo-repo:ref:refs/heads/main"]
}
}
}

resource "aws_iam_role" "github_deploy" {
name = "github-deploy"
assume_role_policy = data.aws_iam_policy_document.github_trust.json
}

The workflow needs id-token: write to request the token (it grants no write access to resources), and contents: read for actions/checkout:

.github/workflows/deploy.yml
permissions:
id-token: write
contents: read

steps:
- uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # commit pinned in GitHub's guide; check for a newer release
with:
role-to-assume: arn:aws:iam::123456789012:role/github-deploy
aws-region: ap-southeast-1
caution

Scope the sub condition. A wildcard such as repo:octo-org/octo-repo:* lets any branch, pull request merge branch or environment of that repository assume the role. For a GitHub environment the value looks like repo:ORG/REPO:environment:NAME.

caution

The aws_iam_openid_connect_provider resource treats thumbprint_list as optional, and for GitHub AWS validates the provider with its own library of trusted root certificate authorities, so a configured thumbprint is not used. An account can have 100 OIDC providers by default, and two providers cannot share a URL.

Session lifetime​

  • aws sts assume-role returns credentials for 1 hour by default. Ask for 900 seconds (15 minutes) up to 43,200 seconds (12 hours) with --duration-seconds, never more than the role's own maximum session duration (1 to 12 hours).
  • With role chaining (a role that assumes another role), the session is limited to 1 hour.
  • The maximum session duration of a role does not limit sessions that AWS services assume for you, such as an instance profile or a Lambda execution role.

IAM policies​

  1. Open the IAM console, choose Policies, then Create policy.

  2. In the Policy editor, choose JSON and enter the policy. This one lets the identity read objects from one bucket and nothing else:

    {
    "Version": "2012-10-17",
    "Statement": [
    {
    "Effect": "Allow",
    "Action": ["s3:GetObject"],
    "Resource": "arn:aws:s3:::my-app-bucket/*"
    }
    ]
    }
    caution

    Name the exact actions and the exact resources. "Action": "*" with "Resource": "*" is the whole account. When you edit in the console, IAM Access Analyzer gives recommendations before you save: read them.

  3. Choose Next, enter a Policy name and an optional Description, review the permissions, then choose Create policy.

    caution

    A customer managed policy can be at most 6,144 characters (white space is not counted), and IAM keeps up to 5 versions of it before you must overwrite one.

  4. Attach it: open the role (or the group), choose Add permissions, then Attach policies, and select the policy (verify the exact labels in the current console).

    caution

    By default a role can have 20 managed policies attached and a user 10. Give a role only the policies it needs, and write one tighter policy instead of attaching many broad ones.

Policy types​

  • Identity-based policies are attached to a user, group or role and say what that identity may do. They are either AWS managed (for common cases, but not always least privilege), customer managed (yours, reusable) or inline (embedded in one identity).
  • Resource-based policies are attached to a resource and name who may use it, for example an S3 bucket policy. See Storage (bucket policies). A role's trust policy is a resource-based policy too.
  • Permissions boundaries set the most an identity-based policy can grant a user or role. They grant nothing on their own.
  • Service control policies (SCPs) and resource control policies (RCPs) in AWS Organizations set guardrails for the accounts below them. They grant nothing on their own.
  • Session policies limit a role session or federated user session further when you ask for temporary credentials.

How a request is evaluated​

By default every request is denied (an implicit deny), except the root user of the account. A request is allowed only if the policies that apply allow it and nothing denies it. The checks run in this order:

  1. Explicit deny: AWS looks in all applicable policies (SCPs, RCPs, resource-based, identity-based, boundaries, session policies) for a Deny that matches. One match and the answer is Deny.
  2. RCPs: if RCPs apply and none allows the action, Deny.
  3. SCPs: if SCPs apply and none allows the action, Deny.
  4. Resource-based policies: in the same account, an Allow here can be enough on its own. A role trust policy and a KMS key policy are the exceptions: they must explicitly allow the principal.
  5. Identity-based policies: if no statement allows the action, Deny.
  6. Permissions boundary: if one exists and does not allow the action, Deny.
  7. Session policy: if one was passed and does not allow the action, Deny. Otherwise, Allow.

Short version: an explicit deny always wins; then everything that applies (organization guardrails, boundary, session policy) must also allow it; and inside one account, for most resources an allow from either an identity-based or a resource-based policy is enough.

caution

Within one account, a resource-based policy that names an IAM user or a role session directly can allow access even when the identity-based policy does not, so check both when a call is allowed and you did not expect it. This page covers the single-account case: cross-account requests follow their own rules (see the cross-account policy evaluation logic page in the IAM User Guide).

Least privilege​

  • Grant only the actions needed, on the specific resources, under conditions where you can (for example, require TLS).
  • Start from AWS managed policies for common cases, then replace them with customer managed policies cut to your workload.
  • Let IAM Access Analyzer generate a policy from the access activity logged in CloudTrail for a role, test it, then use it.
  • Use last accessed information to find and remove users, roles, permissions and credentials you no longer use.
  • Use a permissions boundary to let others create roles for their workloads without letting them exceed a ceiling you set.

Limits​

Defaults per account (the adjustable ones can be raised through Service Quotas):

  • Users per account: 5,000 (not adjustable). Access keys per user: 2. MFA devices per user: 8. Groups per user: 10.
  • Roles per account: 1,000. Role trust policy length: 2,048 characters (up to 8,192). Instance profiles per account: 1,000. Role name: 64 characters.
  • Customer managed policies per account: 1,500. Managed policy size: 6,144 characters. Versions per managed policy: 5.
  • Managed policies attached: 20 per role, 10 per user, 10 per group.
  • Inline policies, total per identity: user 2,048 characters, role 10,240, group 5,120.

Connects to​

  • Compute: Compute is where an instance profile or a Lambda execution role is attached to the thing that runs your code.
  • Storage: Storage (bucket policies) are resource-based policies that decide who can read or write a bucket.
  • Foundations: Workload identity explains why code gets a role instead of keys.
  • Authentication flows: OAuth, JWT and refresh tokens, the ideas behind the OIDC token a CI job trades for AWS credentials.
  • Authentication cheatsheet: a quick reference for those flows.