AWS IAM Security Guide
AWS IAM Security Guide
AWS Identity and Access Management is the security foundation of every AWS account. Every API call, every console login, every service interaction passes through IAM for authentication and authorization. Whether you are a solo developer managing a personal account or an engineer operating hundreds of accounts in an enterprise organization, IAM determines who can do what across your entire cloud infrastructure. Getting IAM right means your resources stay protected. Getting it wrong means data breaches, unauthorized access, and compliance failures that can cost millions.
IAM is not just another AWS service you configure once and forget. It is the control plane that governs access to every other service. When you create an EC2 instance, IAM determines who can launch it. When an application reads from S3, IAM validates whether that application has permission. When a developer logs into the console, IAM authenticates their identity and enforces what they can see and modify. Understanding IAM deeply is a prerequisite for working with any other AWS service securely.
This guide covers IAM comprehensively, from the fundamental building blocks of users, groups, and roles through policy language and evaluation logic, to advanced patterns like cross-account access, permission boundaries, and service control policies. By the end, you will have the knowledge to design and implement IAM configurations that follow the principle of least privilege while remaining operationally manageable. If you are following the AWS services roadmap, IAM should be the first service you master because every other service depends on it for security.
What You Will Learn
After completing this guide, you will have a thorough understanding of AWS IAM and how to apply it in production environments. Specifically, you will learn:
- How IAM authenticates identities through users, federated identities, and temporary credentials, and why the root account should never be used for daily operations
- How IAM authorizes actions through policies, including the JSON policy language, evaluation logic, and the difference between identity-based and resource-based policies
- How IAM groups simplify permission management by letting you assign policies to collections of users rather than managing permissions individually
- How IAM roles provide temporary credentials for applications, services, and cross-account access without storing long-lived secrets
- How to write effective IAM policies using conditions, resource constraints, and the principle of least privilege to minimize your attack surface
- How multi-factor authentication adds a critical second layer of protection for sensitive operations and console access
- How cross-account access patterns work using role assumption, resource-based policies, and AWS Organizations service control policies
- How permission boundaries and session policies create guardrails that prevent privilege escalation even when administrators make mistakes
- How to audit and monitor IAM activity using CloudTrail, Access Analyzer, and credential reports to maintain security posture over time
Each section builds progressively, so reading from start to finish gives you the most complete understanding of how IAM components interact.
Prerequisites
Before working through this guide, ensure you have the following:
- An active AWS account where you have administrative access to create and manage IAM resources for learning purposes
- The AWS CLI installed and configured with credentials using
aws configure, so you can execute IAM commands from your terminal - Basic familiarity with Linux developer commands including working with JSON output, piping commands, and navigating the terminal
- A general understanding of authentication versus authorization concepts, meaning you know the difference between proving who you are and determining what you are allowed to do
- Comfort reading and writing JSON, as IAM policies are expressed entirely in JSON syntax
No prior IAM experience is required. If you have ever managed user accounts or file permissions on any system, the concepts will feel familiar, though IAM operates at a much larger scale with more sophisticated controls.
Concept Overview
IAM operates on four fundamental concepts: identities, policies, authentication, and authorization. Every interaction with AWS involves all four, whether the caller is a human using the console, an application calling an API, or an AWS service acting on your behalf.
Identities in IAM come in three forms. IAM users are permanent identities with long-lived credentials, suitable for human operators who need console access or programmatic access keys. IAM roles are identities that provide temporary credentials through a process called assumption, suitable for applications, services, and cross-account access. Federated identities come from external identity providers like Active Directory, Okta, or Google Workspace and map to IAM roles through trust relationships.
Policies are JSON documents that define permissions. They specify which actions are allowed or denied on which resources under which conditions. IAM evaluates all applicable policies whenever a request is made and produces a final allow or deny decision. The default is deny, meaning if no policy explicitly allows an action, it is denied. An explicit deny in any policy always overrides any allow.
Authentication is the process of proving identity. For console users, this means a username and password plus optional MFA. For programmatic access, this means access key ID and secret access key signed into every API request using AWS Signature Version 4. For roles, this means presenting a valid token obtained through the Security Token Service after a successful assume-role call.
Authorization happens after authentication succeeds. IAM collects all policies that apply to the authenticated identity, evaluates them against the requested action, resource, and conditions, and produces a single allow or deny decision. This evaluation considers identity-based policies, resource-based policies, permission boundaries, session policies, service control policies, and VPC endpoint policies, in a defined precedence order.
Step-by-Step Explanation
This section walks through the essential implementation steps in order. Each step builds on the previous one, providing a clear path from initial configuration to a production-ready setup that follows AWS best practices.
Understanding IAM Users and the Root Account
Every AWS account starts with a root user that has unrestricted access to everything in the account. The root user is identified by the email address used to create the account and authenticated by a password plus optional MFA. The single most important IAM security practice is to never use the root account for daily operations. Lock it away with a strong password and hardware MFA device, and only use it for the handful of tasks that require root access, such as changing account settings or closing the account.
Instead of using root, create individual IAM users for every person who needs access. Each IAM user gets their own credentials, their own permissions, and their own audit trail in CloudTrail. This separation means you can revoke one person's access without affecting anyone else, track exactly who performed each action, and enforce different permission levels for different team members.
IAM users can have two types of credentials. Console access uses a username and password for logging into the AWS Management Console. Programmatic access uses an access key ID and secret access key pair for making API calls through the CLI, SDKs, or direct HTTP requests. A user can have both, either, or neither type of credential depending on their needs.
Working with IAM Groups
Managing permissions for individual users becomes unmanageable as your team grows. If you have twenty developers who all need the same permissions, attaching the same policies to twenty users means twenty updates whenever permissions change. IAM groups solve this by letting you attach policies to a group and then add users to that group. Every user in the group inherits the group's policies.
A common group structure includes a Developers group with permissions to deploy applications and read logs, an Operations group with broader infrastructure permissions, a ReadOnly group for auditors and stakeholders who need visibility without modification rights, and an Administrators group with full access for senior engineers who manage the account itself.
Users can belong to multiple groups, and their effective permissions are the union of all policies from all their groups plus any policies attached directly to the user. This additive model means you can compose permissions from multiple groups without conflicts, though you should prefer group-based permissions over direct user attachments for maintainability.
IAM Roles and Temporary Credentials
Roles are the most important IAM concept for production workloads. Unlike users, roles do not have permanent credentials. Instead, an entity assumes a role and receives temporary credentials that expire after a configurable duration. This eliminates the risk of leaked long-lived credentials and provides automatic credential rotation without any application changes.
Every application running on AWS compute should use a role, never access keys. EC2 instances use instance profiles that automatically deliver role credentials through the instance metadata service. ECS tasks use task roles. Lambda functions use execution roles. In each case, the application code calls AWS APIs without managing credentials at all because the SDK automatically retrieves temporary credentials from the local metadata endpoint.
Roles have two key components: a trust policy that defines who can assume the role, and permission policies that define what the role can do once assumed. The trust policy is a resource-based policy attached to the role itself, specifying which principals (users, services, or accounts) are allowed to call sts:AssumeRole on it.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEC2ToAssumeRole",
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}This trust policy allows the EC2 service to assume the role, which is what makes instance profiles work. When you launch an EC2 instance with an instance profile, the EC2 service assumes the associated role and makes temporary credentials available to applications running on that instance through the metadata service at the address 169.254.169.254.
Writing Effective IAM Policies
IAM policies are JSON documents with a specific structure. Every policy contains a Version field (always use "2012-10-17"), and one or more Statement blocks. Each statement specifies an Effect (Allow or Deny), one or more Actions (API operations like s3:GetObject), one or more Resources (ARNs identifying what the actions apply to), and optional Conditions that further restrict when the statement applies.
The principle of least privilege means granting only the minimum permissions required for a task. In practice, this means specifying exact actions rather than wildcards, constraining resources to specific ARNs rather than using *, and adding conditions that limit when permissions apply. A policy granting s3:* on * violates least privilege. A policy granting s3:GetObject on arn:aws:s3:::myapp-assets/* with a condition requiring the request come from a specific VPC follows it.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowReadAppAssets",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::myapp-prod-assets",
"arn:aws:s3:::myapp-prod-assets/*"
],
"Condition": {
"StringEquals": {
"aws:RequestedRegion": "ap-south-1"
}
}
},
{
"Sid": "DenyUnencryptedUploads",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::myapp-prod-assets/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
}
]
}This policy demonstrates two important patterns. The first statement grants read access to a specific bucket only when requests originate from a specific region. The second statement explicitly denies any upload that does not use KMS encryption, ensuring data at rest is always encrypted regardless of what other policies might allow. Explicit deny statements are powerful guardrails because they override any allow from any other policy.
Policy Evaluation Logic
When IAM receives a request, it evaluates all applicable policies in a specific order to reach a final decision. Understanding this order is essential for debugging access issues and designing effective permission architectures.
The evaluation starts with an implicit deny on everything. Then IAM checks service control policies from AWS Organizations, which can restrict what the entire account is allowed to do. Next it evaluates resource-based policies attached to the target resource. Then it evaluates identity-based policies attached to the calling principal. Permission boundaries are checked next, acting as a ceiling on what identity-based policies can grant. Finally, session policies apply if the caller is using temporary credentials from an assumed role with session restrictions.
The key rules are: an explicit deny anywhere in the chain always wins. An allow is only effective if no deny overrides it and if the action falls within any applicable permission boundary. If nothing explicitly allows the action, the implicit deny applies. This means you can use deny statements as absolute guardrails that no other policy can override.
Multi-Factor Authentication
MFA adds a second authentication factor beyond passwords or access keys. AWS supports virtual MFA devices like Google Authenticator or Authy, hardware TOTP tokens, and FIDO2 security keys. For the root account, always use a hardware device because virtual MFA on a phone can be compromised if the phone is lost or the authenticator app is backed up to a cloud service.
You can enforce MFA through IAM policies by adding a condition that checks whether the request was authenticated with MFA. This is commonly used to require MFA for sensitive operations like deleting resources, modifying security groups, or accessing production environments, while allowing routine read operations without MFA.
A common pattern is to grant users broad permissions but require MFA for everything except managing their own MFA device and password. This means a user who logs in without MFA can only set up their MFA device, and once MFA is configured and verified, they gain their full permissions. This self-service approach scales well because administrators do not need to manually configure MFA for each user.
Cross-Account Access
As organizations grow, they typically operate multiple AWS accounts for isolation between environments, teams, or workloads. Cross-account access lets identities in one account access resources in another without creating duplicate users or sharing credentials.
The standard pattern uses role assumption. Account A creates a role with a trust policy that allows principals from Account B to assume it. Users or roles in Account B call sts:AssumeRole with the role ARN from Account A, receive temporary credentials scoped to Account A, and use those credentials to access resources. This provides a clean audit trail showing exactly who assumed which role and when.
Resource-based policies offer an alternative for services that support them, like S3 and KMS. Instead of assuming a role, the calling principal accesses the resource directly, and the resource policy in the target account grants permission based on the caller's ARN. This is simpler for single-resource access but does not scale as well when access to multiple resources is needed.
AWS Organizations service control policies provide account-level guardrails that restrict what any principal in a member account can do, regardless of their IAM permissions. SCPs do not grant permissions; they only restrict them. This makes them ideal for enforcing organizational security baselines like preventing regions from being used, blocking certain services, or requiring encryption on all storage.
Real-World Use Cases
IAM patterns appear in every production AWS deployment. Understanding these common scenarios helps you design appropriate access controls for your own systems.
Application deployment pipelines use IAM roles to grant CI/CD systems like Jenkins permission to deploy code without storing long-lived credentials. The Jenkins server assumes a deployment role that has permission to update ECS services, push to ECR, invalidate CloudFront caches, and upload to S3. The role's permissions are scoped to exactly what the pipeline needs, and temporary credentials mean a compromised build server cannot maintain access after the credentials expire.
Multi-account organizations use AWS Organizations with service control policies to enforce security baselines across dozens or hundreds of accounts. A common structure separates workloads into production, staging, and development accounts, with a shared services account for logging and security tooling. Cross-account roles let operations teams access any account while maintaining separate blast radii for each environment.
Serverless applications use execution roles to grant Lambda functions access to the specific resources they need. A function that processes uploaded images might have a role allowing s3:GetObject on the upload bucket, s3:PutObject on the processed bucket, and dynamodb:PutItem on a metadata table. Each function gets its own role with minimal permissions rather than sharing a broad role across all functions.
Federated access integrates corporate identity providers with AWS through SAML 2.0 or OpenID Connect. Employees authenticate against Active Directory or Okta, and the identity provider maps their group memberships to IAM roles. This eliminates the need to create IAM users for every employee, provides single sign-on, and automatically revokes AWS access when someone leaves the organization because their corporate identity is disabled.
Temporary elevated access uses role assumption with session duration limits for break-glass scenarios. An on-call engineer normally has read-only access but can assume an elevated role with broader permissions when responding to incidents. The assumption is logged in CloudTrail, the credentials expire after a short duration, and the elevated role can require MFA for assumption, creating a complete audit trail of privileged access.
Best Practices
These practices represent battle-tested patterns for managing IAM securely at scale:
Never use the root account for anything except the few tasks that absolutely require it. Enable MFA on root immediately after account creation, store the MFA device securely, and create IAM users or federated access for all daily operations. Consider not creating access keys for root at all.
Follow the principle of least privilege by starting with zero permissions and adding only what is needed. Use AWS Access Analyzer to identify unused permissions and tighten policies over time. Review permissions quarterly and remove access that is no longer needed. It is always easier to add permissions when someone requests them than to clean up overly broad access after a security incident.
Use roles instead of access keys for every application running on AWS compute. EC2 instance profiles, ECS task roles, Lambda execution roles, and EKS service accounts all provide automatic credential rotation without application changes. If you find yourself creating access keys for an application, ask whether a role would work instead.
Organize users into groups and attach policies to groups rather than individual users. This makes permission changes predictable and auditable. When a new team member joins, adding them to the appropriate groups grants exactly the right permissions. When someone changes teams, moving them between groups adjusts their access cleanly.
Use permission boundaries to delegate user creation safely. A permission boundary sets the maximum permissions that any policy can grant to a user or role. This lets you allow team leads to create roles for their applications without risking that they accidentally create an administrator role. The boundary ensures that even if someone attaches AdministratorAccess to a role, the effective permissions are limited to what the boundary allows.
Enable CloudTrail in every account and every region. CloudTrail records every API call with the identity that made it, the action performed, the resources affected, and the source IP. This audit trail is essential for security investigations, compliance audits, and understanding who changed what when something breaks.
Use IAM Access Analyzer to continuously monitor for unintended external access. Access Analyzer examines resource-based policies across your account and flags any resource that is accessible from outside your account or organization. This catches misconfigured S3 bucket policies, KMS key policies, and role trust policies before they become security incidents.
Rotate credentials regularly and remove unused credentials aggressively. Generate credential reports to identify access keys that have not been used in 90 days and disable them. For access keys that must exist, rotate them on a regular schedule and update all systems that use them before deactivating the old key.
Common Mistakes
These mistakes appear repeatedly in IAM configurations and understanding them helps you avoid costly security failures:
Using wildcard permissions like "Action": "*" and "Resource": "*" is the most common and dangerous IAM mistake. It grants unrestricted access to everything in the account. Even for administrator roles, prefer AWS managed policies like AdministratorAccess which can be tracked and audited, over inline policies with wildcards that are invisible to automated tools.
Sharing access keys between team members or applications destroys accountability. When multiple people use the same access key, CloudTrail cannot distinguish who performed each action. Create individual users with individual keys, or better yet, use roles that provide temporary credentials tied to specific identities.
Not enabling MFA on the root account leaves your entire AWS account vulnerable to password compromise. A single phished password gives an attacker unrestricted access to everything including billing, account closure, and data destruction. Hardware MFA on root is non-negotiable for any account that matters.
Embedding access keys in application code or committing them to version control is a critical security failure. Secrets scanners on GitHub and GitLab detect AWS keys within seconds of being pushed, and automated attackers exploit them within minutes. Use environment variables, AWS Secrets Manager, or preferably IAM roles that eliminate the need for keys entirely.
Creating overly broad trust policies on roles allows unintended principals to assume them. A trust policy with "Principal": "*" lets anyone in any AWS account assume the role. Always specify exact account IDs, service principals, or ARNs in trust policies, and add conditions like aws:PrincipalOrgID to restrict assumption to your organization.
Ignoring the credential report means stale credentials accumulate indefinitely. Users who left the organization months ago may still have active access keys. Unused keys represent attack surface with zero benefit. Run the credential report monthly and disable any credentials not used in the past 90 days.
Not using service control policies in multi-account environments means individual accounts can do anything AWS allows, regardless of organizational intent. SCPs provide the only mechanism to absolutely prevent actions at the account level, such as blocking access to regions you do not use or preventing services that violate compliance requirements.
Summary
AWS IAM is the security control plane that governs every interaction with your AWS infrastructure. It authenticates identities through users, roles, and federated access. It authorizes actions through a sophisticated policy evaluation engine that considers identity-based policies, resource-based policies, permission boundaries, session policies, and service control policies. It provides temporary credentials through roles that eliminate the risks of long-lived secrets. And it records everything through CloudTrail for audit and compliance.
The key principles to internalize are: never use root for daily operations, always prefer roles over access keys, follow least privilege by starting with zero permissions and adding only what is needed, organize access through groups rather than individual user policies, use permission boundaries to safely delegate, and monitor continuously with Access Analyzer and credential reports. These principles scale from a single developer account to enterprise organizations with hundreds of accounts and thousands of engineers.
As you continue through the AWS services roadmap, you will find that every service relies on IAM for access control. EC2 instances need instance profiles. S3 buckets need bucket policies that interact with IAM policies. Lambda functions need execution roles. Understanding IAM deeply makes learning every other service faster because the permission model is consistent across all of AWS. Your next steps should include exploring how IAM integrates with specific services like S3 for storage access control and how Organizations with SCPs provide governance at scale across multiple accounts.