AWS IAM Explained: Permissions, Roles, and Policies

Every action in AWS is validated by IAM. Learn how users, groups, roles, and policies work — and why least privilege is the highest-ROI security investment in any AWS account.

IAM ENTITY MODEL POLICY JSON Document USER Permanent credentials GROUP Users collection ROLE Temp credentials Lambda · EC2 · ECS · Cross-Account Assumes role → gets temp creds EXPLICIT DENY Always overrides Allow Regardless of other policies
4Core IAM Entities
FreeIAM Service Cost
Deny>AllowPolicy Evaluation Rule
15min–12hRole Credential TTL

In This Guide

  1. What Is IAM and Why It Matters
  2. Core Concepts: Users, Groups, Roles, and Policies
  3. Policies Deep Dive: Allow, Deny, and Condition
  4. Roles Explained: The Right Way to Grant Access
  5. The Least Privilege Principle in Practice
  6. Common IAM Patterns Every Developer Needs
  7. Troubleshooting Access Denied Errors
  8. IAM Best Practices Checklist
  9. Frequently Asked Questions

Key Takeaways

IAM is the most important service in AWS that developers consistently get wrong. Not because it is complicated in theory, but because the natural impulse when you hit an "Access Denied" error is to add more permissions — often far more than you need — until it works. That impulse, repeated across a codebase, creates permission sprawl that makes your cloud environment brittle and your security posture weak.

This guide explains IAM clearly: what each concept means, why roles are better than users for application access, how policies are evaluated, and the patterns you should follow to avoid the most common mistakes.

01

What Is IAM and Why It Matters

AWS Identity and Access Management (IAM) is the system that controls who can authenticate to your AWS account and what actions they are authorized to take. Every API call in AWS — whether from the console, CLI, SDK, or another AWS service — is validated by IAM before it executes.

IAM is global — it is not regional. IAM users, groups, roles, and policies you create in one region are available across all regions in your account. This is different from most AWS services, which are region-scoped.

IAM is free. There is no charge for IAM users, roles, policies, or groups. You pay only for the AWS services that IAM principals call.

"Every high-profile AWS breach of the last five years has involved IAM in some way — either overly permissive roles that allowed lateral movement, or leaked credentials that were not scoped to minimum permissions."

Cloud Security Field Analysis
02

Core Concepts: Users, Groups, Roles, and Policies

IAM has four core entities: users (human identities with permanent credentials), groups (collections of users), roles (assumable identities with temporary credentials), and policies (permission documents attached to any of the above).

👤

IAM Users

Permanent identities for humans. Each gets a console password and/or access keys. Never for applications or automation.

👥

IAM Groups

Collections of users sharing permissions. Attach policies to groups — never to individual users. Scales easily as teams grow.

🔒

IAM Roles

Assumable identities with temporary credentials (15 min–12 hr). Correct mechanism for Lambda, EC2, ECS, and cross-account access.

📄

IAM Policies

JSON documents defining what is allowed or denied. Attached to users, groups, or roles. AWS managed or customer managed.

IAM User

For Humans Only

  • Permanent password + access keys
  • Login to AWS Console
  • Requires manual credential rotation
  • Do NOT create for applications
IAM Role

For Everything Else

  • Temporary credentials auto-issued by STS
  • Assumed by services, users, other accounts
  • Credentials expire automatically
  • Use for Lambda, EC2, ECS, pipelines
03

Policies Deep Dive: Allow, Deny, and Condition

An IAM policy is a JSON document with one or more Statement blocks. Each statement has an Effect (Allow or Deny), an Action (the API calls being permitted or denied), and a Resource (the specific ARN or ARN pattern the statement applies to). Conditions add optional constraints.

JSON — IAM Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-bucket",
        "arn:aws:s3:::my-app-bucket/*"
      ]
    },
    {
      "Effect": "Deny",
      "Action": "s3:DeleteObject",
      "Resource": "arn:aws:s3:::my-app-bucket/*"
    }
  ]
}

This policy allows reading from and listing the bucket, but explicitly denies deleting objects — even if another attached policy would otherwise allow it. Explicit Deny always wins.

Conditions add constraints based on request context: the source IP, time of day, MFA status, request region, or tag values on the resource.

JSON — IP Condition
"Condition": {
  "IpAddress": {
    "aws:SourceIp": [
      "203.0.113.0/24",
      "198.51.100.0/24"
    ]
  }
}

Conditions are powerful for building fine-grained controls: require MFA for all sensitive API calls, restrict console access to corporate IP ranges, enforce tag-based access control so teams can only manage resources with their own team's tags.

04

Roles Explained: The Right Way to Grant Access

IAM roles should be used for all non-human access in AWS. The pattern is simple: create a role with a trust policy (who can assume it) and a permission policy (what they can do when they assume it).

How Lambda uses a role:

  1. Create an IAM role with a trust policy allowing lambda.amazonaws.com to assume it
  2. Attach a permissions policy granting the specific S3, DynamoDB, or other permissions the function needs
  3. Assign the role to the Lambda function
  4. When Lambda executes, it automatically obtains temporary credentials from STS by assuming the role. Your code never needs to manage credentials — the AWS SDK picks them up automatically from the execution environment.

Cross-account roles allow principals in one AWS account to assume roles in another account. This is the correct pattern for giving a developer in your dev account access to read-only resources in your prod account: create a role in prod with a trust policy allowing the dev account, and the developer assumes the role from the dev account's CLI.

Never create IAM users and long-term access keys for automated processes. Access keys can be leaked in source code, environment variable files, log output, or error messages. Roles with temporary credentials cannot be leaked in a way that provides permanent access — the credentials expire automatically.

05

The Least Privilege Principle in Practice

Least privilege means granting only the specific permissions needed for the specific task, on the specific resources involved, for the minimum time required. Nothing more.

In practice, most developers violate least privilege in one of three ways:

1. Using wildcard actions: "Action": "s3:*" grants all S3 actions — including deleting buckets. If your Lambda function only reads from one bucket, it needs only s3:GetObject and s3:ListBucket on that specific bucket's ARN. Not s3:* on *.

2. Using wildcard resources: "Resource": "*" applies the permission to every resource of that type in the account. Grant permissions on specific ARNs whenever possible.

3. Attaching managed policies that include unnecessary permissions: AmazonS3FullAccess includes s3:DeleteBucket. AmazonDynamoDBFullAccess includes the ability to delete tables. Write narrow customer-managed policies for production workloads rather than relying on AWS managed policies.

Tools to help:

06

Common IAM Patterns Every Developer Needs

Pattern 1: Lambda function with S3 and DynamoDB access

JSON — Lambda Role Policy
{
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:PutObject"],
    "Resource": "arn:aws:s3:::my-bucket/*"
  },{
    "Effect": "Allow",
    "Action": [
      "dynamodb:GetItem", "dynamodb:PutItem",
      "dynamodb:UpdateItem", "dynamodb:Query"
    ],
    "Resource": "arn:aws:dynamodb:us-east-1:123456789:table/MyTable"
  },{
    "Effect": "Allow",
    "Action": [
      "logs:CreateLogGroup", "logs:CreateLogStream",
      "logs:PutLogEvents"
    ],
    "Resource": "arn:aws:logs:*:*:*"
  }]
}

Pattern 2: EC2 instance profile for reading Secrets Manager — Create a role, trust ec2.amazonaws.com, attach a policy with secretsmanager:GetSecretValue on the specific secret ARN. Attach the role as an instance profile when launching the EC2 instance.

Pattern 3: Developer read-only access to production — Create a role in the prod account, trust your dev account, attach ReadOnlyAccess managed policy. Developers assume the role with MFA required as a condition. No permanent prod access keys exist.

07

Troubleshooting Access Denied Errors

Access Denied errors in AWS are notoriously terse. Here is the systematic approach to diagnosing them:

Step 1: Identify the exact API call and principal. The CloudTrail log entry for the denied call contains the exact action (s3:PutObject), the exact resource ARN, and the principal that made the call.

Step 2: Check whether it is an identity-based policy issue or a resource-based policy issue. Some AWS services have resource policies (S3 bucket policies, KMS key policies, SQS queue policies) that independently control access. An explicit Deny in the resource policy will override an Allow in the identity policy.

Step 3: Use the IAM Policy Simulator. Navigate to IAM > Policy Simulator, select the principal, enter the action and resource, and simulate. The simulator shows exactly which policy statement is causing the deny.

Step 4: Check for SCPs (Service Control Policies). If your account is part of an AWS Organization, SCP denies at the organizational level override all IAM policies. Your account admin may have applied SCPs that restrict specific services or regions.

Common gotcha: S3 operations require permissions on both the bucket (arn:aws:s3:::bucket-name) and the objects (arn:aws:s3:::bucket-name/*). Granting only one and not the other is a common source of confusing Access Denied errors.

08

IAM Best Practices Checklist

Run through this checklist for every AWS account:

09

Frequently Asked Questions

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

An IAM user has permanent, long-term credentials (password, access keys) and is designed for human beings. An IAM role has no permanent credentials — it issues temporary credentials when assumed, valid for 15 minutes to 12 hours. Roles are the correct mechanism for applications, services, Lambda functions, EC2 instances, and cross-account access.

Can I give an EC2 instance admin access to AWS?

Technically yes, but you should never do this. Attaching AdministratorAccess to an EC2 instance role means that any code running on that instance — including any code injected by an attacker — has full admin access to your AWS account. Always use least-privilege roles with only the permissions the specific application needs.

How do I rotate IAM access keys?

Create a new access key, update all applications using the old key to use the new one, verify the applications work correctly, then deactivate and delete the old key. AWS recommends rotating access keys every 90 days. Better: eliminate long-term access keys entirely by using IAM roles and temporary credentials everywhere.

What is an IAM policy condition?

A condition is an optional clause in an IAM policy statement that adds constraints beyond just the action and resource. Conditions can restrict access based on source IP address, time of day, MFA status, requested region, resource tags, and many other attributes. Use conditions to enforce MFA for sensitive operations or restrict access to specific corporate IP ranges.

Verdict: IAM Is the Highest-ROI Security Investment in AWS

Getting IAM right takes a day to learn and a career to master. The investment pays off immediately: correct least-privilege roles prevent privilege escalation, temporary credentials eliminate the access key leak category of breach, and explicit deny policies create guardrails that survive policy changes. Start with roles over users, work toward specific ARNs in every policy, and run Access Analyzer regularly to catch what you have missed.

IAM is the foundation of AWS security. Get the skills.

Join professionals from Denver, NYC, Dallas, LA, and Chicago for a 2-day in-person AI training bootcamp. $1,490. June–October 2026 (Thu–Fri). Seats are limited.

Reserve Your Seat
PA
Our Take

IAM misconfiguration is still the most common root cause of AWS security incidents — and AI agents make it worse.

AWS IAM is technically well-designed: the principle of least privilege is the right mental model, conditions give granular control, and the policy evaluation logic is auditable. The problem is that getting IAM right requires constant vigilance against a natural human tendency to grant broader permissions to make a thing work quickly and then never revise them. Overly permissive IAM roles — the `*` wildcard Action, the IAM role that can describe every resource in the account — are how the majority of AWS data breaches originate, according to multiple years of Verizon DBIR and Datadog's State of Cloud Security reports.

The issue is compounding in the AI era for a specific reason: AI coding assistants generate IAM policy snippets that tend toward broad permissions, because training data from Stack Overflow and GitHub tutorials historically used broad permissions as examples. When Claude Code or Copilot writes `"Action": "s3:*"` to "get the IAM working," and a developer copies that into production without revision, you get a classic overpermission pattern. AWS IAM Access Analyzer helps, and AWS is adding AI-assisted policy review tooling, but the root discipline of writing scoped policies from the start is what matters.

The one IAM practice that eliminates more risk than any other: never allow IAM roles to create or attach IAM policies to themselves or other principals. That boundary, rigorously maintained, prevents most privilege escalation paths. Add a Service Control Policy at the organization level that enforces it, and you've closed the most dangerous attack class in AWS environments.

PA

Published By

Precision AI Academy

Practitioner-focused AI education · 2-day in-person bootcamp in 5 U.S. cities

Precision AI Academy publishes deep-dives on applied AI engineering for working professionals. Founded by Bo Peng (Kaggle Top 200) who leads the in-person bootcamp in Denver, NYC, Dallas, LA, and Chicago.

Kaggle Top 200Federal AI Practitioner5 U.S. CitiesThu–Fri Cohorts