In This Guide
- What Is IAM and Why It Matters
- Core Concepts: Users, Groups, Roles, and Policies
- Policies Deep Dive: Allow, Deny, and Condition
- Roles Explained: The Right Way to Grant Access
- The Least Privilege Principle in Practice
- Common IAM Patterns Every Developer Needs
- Troubleshooting Access Denied Errors
- IAM Best Practices Checklist
- Frequently Asked Questions
Key Takeaways
- Identity is the perimeter: In AWS, every action requires authentication (who are you?) and authorization (what are you allowed to do?). IAM is the system that answers both questions.
- Use roles, not users, for machines: EC2 instances, Lambda functions, and ECS tasks should use IAM roles (temporary credentials) never long-term access keys. Roles are more secure and easier to manage.
- Least privilege is not optional: Every IAM policy should grant only the specific permissions needed for the specific task. Over-permissioned roles are the most common cause of cloud breaches.
- Deny overrides Allow: In IAM, an explicit Deny statement always overrides any Allow. Use this to add guardrails for sensitive resources regardless of other policies attached.
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 a 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.
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.
Why IAM matters for security: 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. Getting IAM right is the highest-ROI security investment in any AWS account.
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 are permanent identities created for human beings who need long-term access to AWS. Each user gets a password for console access and/or access keys (access key ID + secret access key) for programmatic access. IAM users should only exist for human beings — never for applications, services, or automation.
IAM Groups are collections of IAM users that share the same permissions. Best practice: never attach policies directly to individual users. Create groups (Developers, ReadOnlyAuditors, DBAdmins), attach policies to groups, and add users to groups. This makes permission management scalable — when a developer leaves, you remove them from the group instead of hunting through individual policy attachments.
IAM Roles are identities that can be assumed by AWS services, users, or other accounts — and that provide temporary credentials (15 minutes to 12 hours). This is the correct mechanism for all non-human access: your Lambda function assumes a role, your EC2 instance assumes a role, a developer in account A assumes a role in account B. Roles are more secure than users for application access because temporary credentials cannot be leaked and used indefinitely.
IAM Policies are JSON documents that define what actions are allowed or denied on which resources. Policies are attached to users, groups, or roles to define their permissions. There are two types: AWS managed policies (pre-built by AWS, like AmazonS3ReadOnlyAccess) and customer managed policies (policies you write for your specific needs).
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.
{
"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. Example condition restricting access to specific IP ranges:
"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.
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). The service or user assumes the role and receives temporary credentials.
How Lambda uses a role:
- Create an IAM role with a trust policy allowing
lambda.amazonaws.comto assume it - Attach a permissions policy granting the specific S3, DynamoDB, or other permissions the function needs
- Assign the role to the Lambda function
- 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.
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:
- IAM Access Analyzer: Identifies resources that are accessible from outside your account and generates least-privilege policies based on CloudTrail logs.
- IAM Policy Simulator: Test what a specific user or role can and cannot do before deploying.
- AWS CloudTrail: Review which API calls a role actually makes in production and trim the policy to match actual usage.
Common IAM Patterns Every Developer Needs
Pattern 1: Lambda function with S3 and DynamoDB access
{
"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.
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 (the IAM user, role, or assumed role session) that made the call. If CloudTrail is enabled, you can find this in the CloudTrail console or Athena.
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.
IAM Best Practices Checklist
Run through this checklist for every AWS account:
- Enable MFA on the root account immediately. Lock the root access keys in a vault.
- Create individual IAM users for each human. Never share credentials.
- Use groups to assign permissions. Never attach policies directly to individual users.
- Use roles for all applications, services, and automation. No long-term access keys for machines.
- Require MFA for all console-access users.
- Enable IAM Access Analyzer and review findings quarterly.
- Enable AWS CloudTrail in all regions to log all API calls.
- Set a password policy: minimum 14 characters, require complexity, 90-day rotation.
- Delete or deactivate unused IAM users and access keys. AWS Security Hub has a finding for credentials unused for 90+ days.
- Never use the root account for daily operations. Create an admin IAM user for account management tasks.
- Review and trim IAM policies annually using Access Analyzer's unused access findings.
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.
IAM is the foundation of AWS security. Get the skills.
Join professionals from Denver, NYC, Dallas, LA, and Chicago for two days of hands-on AI and tech training. $1,490. October 2026. Seats are limited.
Reserve Your SeatNote: Information in this article reflects the state of the field as of early 2026. Technology evolves rapidly — verify specific version numbers, pricing, and service availability directly with vendors before making decisions.