AWS IAM Explained: Users, Roles, Policies and Permissions

AWS Identity and Access Management (IAM) is the service that controls who can access your AWS account and what they are allowed to do inside it. Every action in AWS goes through IAM for a permission check before it executes, whether that action is launching an EC2 instance, reading a file from S3, or creating a database.

IAM matters because misconfigured permissions are the single most common cause of both security incidents and broken deployments in AWS. An over-permissioned role can give an attacker full account access. An under-permissioned role can block your application from starting. Getting IAM right from the beginning saves hours of debugging and prevents real security risk.

By the end of this page you will understand what IAM is, how its building blocks fit together, how AWS evaluates permissions, and how to avoid the mistakes that trip up most beginners.

What AWS IAM is

IAM handles two things: authentication (proving you are who you say you are) and authorization (deciding whether you have permission to do what you are trying to do). When you sign into the AWS Management Console, IAM authenticates you. When you run an AWS CLI command, IAM checks whether your identity has permission to perform that action.

IAM is a global service. Unlike most AWS services that are scoped to a specific region, the users, roles, and policies you create in IAM are available across every region in your account.

AWS IAM explained simply

Analogy

Think of your AWS account as an office building. IAM is the security desk at the entrance. When someone walks in, the desk checks their ID badge (authentication). Then it checks the access list to see which floors and rooms that person is allowed to enter (authorization). No badge, no entry. Badge without the right access level, you get stopped at the door to that specific room. IAM works the same way for every API call in AWS.

Why IAM matters

IAM is not optional and it is not something you configure once and forget. Every AWS account uses it whether you realize it or not.

  • Security. IAM is where you enforce least privilege, giving each identity only the permissions it needs. A Lambda function that only reads from one S3 bucket should not have permission to delete EC2 instances.

  • Operational stability. Under-permissioned roles break deployments. If your CI/CD pipeline’s role cannot push to ECR or update a Lambda function, the deployment fails silently or with a cryptic AccessDenied error.

  • Auditability. Combined with CloudTrail, IAM gives you a record of who did what, when, and whether it was allowed. That audit trail is essential for incident response and compliance.

IAM mistakes are expensive. In 2019, Capital One suffered a breach affecting 100 million customers because an overly permissive IAM role on a WAF allowed an attacker to access S3 buckets containing sensitive data. The root cause was not a software vulnerability. It was an IAM configuration error. Getting permissions right is not just best practice; it is the difference between a secure account and an incident.

Core IAM building blocks

Users

An IAM user is a permanent identity that represents a single person. Each user has their own credentials: a password for console access and optionally access keys for CLI and API access. Users are fine for human operators, but long-lived access keys are a security risk and should be avoided where possible.

Groups

A group is a collection of users. Policies attached to a group apply to every user in it. Instead of attaching the same policy to ten developers individually, create a Developers group and attach the policy once. When someone joins the team, add them to the group. When they leave, remove them.

Roles

An IAM role is an identity that issues temporary credentials when assumed. Roles are not tied to a specific person. They are the preferred way to grant permissions to AWS services (a Lambda function, an EC2 instance), to applications, and to users who need cross-account access. Temporary credentials expire automatically, which makes roles inherently safer than long-lived user credentials. See IAM Roles Explained for a deeper look.

Tip

When in doubt, use a role. AWS services like Lambda, EC2, and ECS all support roles natively. You never need to hardcode credentials when a role is available. If you find yourself creating access keys for an application running on AWS, stop and use a role instead.

Policies

A policy is a JSON document that defines what actions are allowed or denied on which resources. Policies are the rules. They do nothing on their own and only take effect when attached to a user, group, or role. For a full breakdown of policy syntax, see IAM Policy Structure.

Resources

Resources are the AWS objects being acted upon: an S3 bucket, an EC2 instance, a DynamoDB table. In policies, resources are identified by their ARN (Amazon Resource Name), like arn:aws:s3:::my-app-bucket/*.

Actions

Actions are the specific API operations being controlled. The format is service:OperationName. Examples: s3:GetObject, ec2:StartInstances, iam:CreateRole.

Conditions

Conditions add extra constraints to a policy statement. You can restrict access based on IP address, time of day, whether MFA was used, or dozens of other context keys. Conditions let you write policies like “allow this action only if the request comes from the corporate network.” See IAM Policy Conditions for details and examples.

How IAM permission evaluation works

Analogy

Imagine a bouncer at a nightclub with a clipboard. The default rule is “nobody gets in.” The clipboard has two sections: an allow list and a block list. The bouncer checks the block list first. If your name is on it, you are turned away immediately, no matter what the allow list says. If you are not blocked, the bouncer checks the allow list. If your name is there, you get in. If your name is not on either list, you are turned away by default. That is exactly how IAM evaluates every API call.

When any principal makes an API call, IAM evaluates all applicable policies to decide whether the action is allowed or denied. The logic follows a specific order:

  1. Start with default deny. Everything is denied unless explicitly allowed. A missing policy is a deny, not a gap.
  2. Check for explicit deny. If any attached policy has “Effect”: “Deny” for this action, the request is denied immediately. Explicit denies always win.
  3. Check Service Control Policies (SCPs). If the account is part of AWS Organizations, SCPs set the maximum permissions for the entire account. If an SCP does not allow the action, it is denied regardless of IAM policies.
  4. Check permission boundaries. If the role or user has a permission boundary set, the action must be allowed by both the boundary and the identity policy. The boundary acts as a ceiling.
  5. Check identity-based policies. Is there an “Effect”: “Allow” for this action on this resource in the user’s, group’s, or role’s attached policies?
  6. Check resource-based policies. Does the resource itself (like an S3 bucket policy) grant access to this principal?
  7. If no allow was found, deny. The default deny applies.
Note

The key takeaway: AWS denies by default, and an explicit “Effect”: “Deny” always overrides any “Effect”: “Allow”. You do not need to write deny statements for most things. Just do not write an allow. Use explicit denies only when you need to override an allow that comes from another policy.

IAM users vs groups vs roles

FeatureIAM UserIAM GroupIAM Role
What it representsA single personA collection of usersAn identity anyone or anything can assume
CredentialsPassword and/or access keys (long-lived)None (users in the group use their own)Temporary credentials via STS
Best forHuman console/CLI accessOrganizing users with shared permissionsServices, applications, cross-account access
Credential rotationManualN/AAutomatic (credentials expire)

The general rule: use users for humans who need console or CLI access, groups to manage those users’ permissions at scale, and roles for everything else. If you are granting permissions to an AWS service or an application, always use a role. For a detailed comparison, see IAM Users vs Roles.

Identity-based vs resource-based policies

AspectIdentity-based policyResource-based policy
Attached toA user, group, or roleA resource (S3 bucket, SQS queue, KMS key)
SpecifiesWhat this identity can doWho can access this resource
Cross-accountRequires role assumptionCan grant access directly to another account
Common exampleA policy on a role allowing s3:GetObjectAn S3 bucket policy allowing a specific role to read objects
Analogy

Identity-based policies are like a keycard that a person carries. The keycard defines which doors that person can open. Resource-based policies are like a lock on a specific door that has its own list of who is allowed through. You can grant access from either side: give the person a keycard for that door, or add the person to the door’s allow list.

S3 example. You can grant a role access to an S3 bucket in two ways. An identity-based policy on the role says “this role can read from this bucket.” A resource-based policy (bucket policy) on the S3 bucket says “this bucket allows this role to read.” Both work. For cross-account access, resource-based policies are often simpler. For a full comparison, see S3 IAM vs Bucket Policies.

AWS also distinguishes between AWS managed policies and customer managed policies. AWS managed policies cover common use cases and are maintained by AWS. Customer managed policies give you precise control but you maintain them yourself.

When to use this

You will interact with IAM almost immediately when using AWS. Here are the most common situations:

  • Console access. When you create an AWS account, you sign in as the root user. Your first IAM task is creating a separate IAM user for daily work so you stop using root.

  • Application-to-service access. A Lambda function needs to read from DynamoDB or write to S3. You create a role with exactly those permissions and assign it to the function.

  • Cross-account access. Your staging and production environments live in separate accounts. You create a role in the production account that your CI/CD pipeline in the staging account can assume.

  • Temporary credentials. Instead of embedding access keys in your code, use roles and STS temporary credentials that expire automatically.

  • Team management. You have five developers who all need the same permissions. Create a group, attach a policy to it, and add each developer to the group.

Common IAM use cases

A developer signing into the AWS console

An admin creates an IAM user with a password and console access. The user is added to a Developers group that has the PowerUserAccess managed policy attached. The developer signs in through the IAM sign-in URL and can use most AWS services but cannot manage IAM itself.

A Lambda function accessing S3

You create an IAM role with a policy that allows s3:GetObject on a specific bucket. You assign that role as the Lambda function’s execution role. When the function runs, it automatically receives temporary credentials scoped to that role’s permissions. No access keys are stored in code.

An EC2 instance using a role

Instead of putting access keys on the instance, you create an instance profile with an IAM role. The EC2 instance automatically gets temporary credentials from the instance metadata service. Any application running on the instance can use those credentials to call AWS APIs.

Cross-account access via role assumption

Account A needs to read data from a DynamoDB table in Account B. Account B creates a role with the needed permissions and a trust policy that allows Account A to assume it. A principal in Account A calls sts:AssumeRole, gets temporary credentials for Account B’s role, and uses them to read the table. See Role Assumption in AWS for a full walkthrough.

Example: a minimal IAM policy

This policy allows reading and writing objects in one S3 bucket and listing its contents. It grants nothing else.

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

Notice the two separate statements. Object-level actions (GetObject, PutObject) target the objects inside the bucket (/*), while ListBucket targets the bucket itself (no /*). Mixing these up is one of the most common IAM mistakes. For the full syntax breakdown, see IAM Policy Structure.

# Create the policy from a JSON file
aws iam create-policy \
  --policy-name MyAppS3Policy \
  --policy-document file://my-policy.json

# Attach it to a role
aws iam attach-role-policy \
  --role-name my-app-role \
  --policy-arn arn:aws:iam::123456789012:policy/MyAppS3Policy

Common beginner mistakes

  1. Overusing “Resource”: ”*”. A wildcard resource grants the action against every resource of that type in your account. Always scope resources to specific ARNs. If you need multiple resources, list them explicitly.

  2. Attaching AdministratorAccess too broadly. Giving a Lambda function or an EC2 instance AdministratorAccess means a compromised workload has full control of your account. Start with the narrowest permissions and broaden only as needed.

  3. Confusing “not allowed” with explicit deny. By default everything is denied in AWS. You do not need to write explicit deny statements for most things. Just do not write an allow. Use explicit deny only when you need to override an allow from another policy.

  4. Using long-lived access keys where roles work. Access keys do not expire automatically and must be rotated manually. For applications running on AWS (Lambda, EC2, ECS), always use roles with temporary credentials instead. See why long-lived access keys are dangerous.

  5. Not testing policies before production. Use the IAM Policy Simulator in the console or the simulate-principal-policy CLI command to verify that a policy does what you expect before attaching it to a production role.

  6. Ignoring the root account. The root account has unrestricted access to everything. Enable MFA on it, do not use it for daily work, and create IAM users or roles for all regular operations.

How to debug AccessDenied errors

AccessDenied is one of the most common errors in AWS. Here is a practical checklist for diagnosing it:

  1. Identify the principal. Which user or role made the request? Check the error message or CloudTrail event for the ARN.
  2. Check identity-based policies. Does the user or role have a policy that allows this specific action on this specific resource ARN?
  3. Check resource-based policies. If the resource (S3 bucket, KMS key, SQS queue) has its own policy, does it allow this principal?
  4. Check for explicit denies. Look at all attached policies, SCPs, and permission boundaries for any “Effect”: “Deny” that matches the action.
  5. Verify the resource ARN. A common mistake is targeting the bucket ARN when the action requires the object ARN (with /*), or vice versa.
  6. Check SCPs and permission boundaries. If the account is in an Organization, an SCP may block the action even if the IAM policy allows it.
  7. Simulate the policy. Use the IAM Policy Simulator to test whether the action is allowed.
# Simulate whether a specific action is allowed for a role
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:role/my-app-role \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::my-bucket/file.txt \
  --query 'EvaluationResults[*].{Action:EvalActionName,Decision:EvalDecision}' \
  --output table
Tip

Most AccessDenied errors come down to one of three things: a missing allow statement, a mismatched resource ARN, or an SCP that silently blocks the action. Start with those three before investigating anything else. For a deeper dive with walkthroughs, see IAM AccessDenied Errors.

Frequently asked questions

What is AWS IAM?

AWS Identity and Access Management (IAM) is the service that controls who can access your AWS account and what they are allowed to do. Every API call to any AWS service is checked by IAM. It handles both authentication (proving your identity) and authorization (deciding whether you have permission). IAM is a global service, so users, roles, and policies you create work across all AWS regions.

What is an IAM policy?

An IAM policy is a JSON document that defines permissions. Each policy contains one or more statements that specify which actions are allowed or denied on which AWS resources, and under what conditions. Policies are attached to users, groups, or roles. AWS provides hundreds of managed policies for common use cases, and you can write your own custom policies for fine-grained control.

What is the difference between an IAM user and an IAM role?

An IAM user is a permanent identity with long-lived credentials (password or access keys) tied to one person. An IAM role is an identity that issues temporary credentials when assumed, and it is not tied to a specific person. Roles are preferred for applications, services, and cross-account access because temporary credentials expire automatically and do not need to be rotated manually.

What does least privilege mean in IAM?

Least privilege means giving an identity only the permissions it needs to do its specific job, and nothing more. Instead of granting broad access like AdministratorAccess, you write policies that allow specific actions on specific resources. This limits the damage if credentials are compromised and reduces the chance of accidental changes to unrelated resources.

Why do I get AccessDenied in AWS?

AccessDenied means IAM evaluated the request and found no policy that allows the action, or found a policy that explicitly denies it. Common causes include missing permissions on the user or role, a restrictive Service Control Policy from AWS Organizations, a permission boundary that limits what the role can do, or targeting the wrong resource ARN. Use the IAM Policy Simulator or CloudTrail logs to identify which policy blocked the request.

Last verified: 29 March 2026 Cloud services change frequently. Verify details against official documentation before making infrastructure decisions.