AWS IAM: Architecting Identity, Access, and Authorization at Scale
AWS IAM policy design, role architecture, CLI operations, security best practices, and integration patterns
Every AWS environment, regardless of scale or complexity, depends on a single foundational service to govern who may access what, and under which conditions. AWS Identity and Access Management — commonly referred to as IAM — serves as the centralized authentication and authorization backbone for the entire AWS ecosystem. It is not optional, nor is it a service you configure once and forget. IAM demands deliberate architectural thinking from the very first resource you provision.
The core challenge IAM addresses is multifaceted: granular permission control across hundreds of AWS services, secure credential lifecycle management, comprehensive access auditing, and the elimination of hardcoded credentials from application code. The relevant question is never whether to use IAM, but rather how to design a permission structure that is both secure and scalable from day one.
IAM is a global service — it is not region-scoped. Users, roles, groups, and policies you create are available across all AWS regions within the account.
Key Concepts
| Concept | Description |
|---|---|
| Policies | JSON documents that define specific permissions — which actions are permitted on which resources. They answer one question: "What can this identity do?" |
| Identity-based Policies | Policies attached to identities such as users, groups, or roles. Example: "This user may read from S3 and write to CloudWatch." |
| Resource-based Policies | Policies attached directly to resources like S3 buckets or Lambda functions. Example: "This S3 bucket permits access from a specific role." |
| Users | Identities representing human operators. They carry permanent credentials — a password, access keys, or both — that persist until manually rotated. |
| Groups | Logical collections of users sharing identical permissions, dramatically simplifying administration at scale. |
| Roles | Identities designed for temporary assumption by AWS services, applications, or cross-account users. They issue temporary credentials that expire and renew automatically. |
| Trust Policy | A specialized policy defining which principals may assume a given role. This is the role's authorization gate — it answers: "Who is permitted to use these permissions?" |
| Instance Profile | A container that makes a role attachable to EC2 instances, serving as the technical bridge between IAM roles and the compute layer. |
| Managed Policies | Pre-built policies maintained by AWS. Examples include AmazonS3ReadOnlyAccess and AmazonSSMManagedInstanceCore. |
| Custom Policies | Policies you author for specific use cases, granting full control over exact permission boundaries. |
| Instance Metadata Service | An internal service at 169.254.169.254 that furnishes temporary credentials to EC2 instances, enabling SDKs like boto3 to obtain credentials without any manual configuration. |
| ARN | Amazon Resource Name — the unique identifier for every AWS resource. Format: arn:aws:service:region:account:resource. |
Essential AWS CLI Commands
aws iam create-policy \
--policy-name MyCustomPolicy \
--policy-document file://policy.json \
--description "Policy description"aws iam create-user \
--user-name developer-johnaws iam create-access-key --user-name developer-johnaws iam create-login-profile \
--user-name developer-john \
--password 'TempPassword123!' \
--password-reset-requiredaws iam create-group --group-name backend-developersaws iam add-user-to-group \
--user-name developer-john \
--group-name backend-developerscat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com"
},
"Action": "sts:AssumeRole"
}]
}
EOFaws iam create-role \
--role-name EC2AccessRole \
--assume-role-policy-document file://trust-policy.json \
--description "Role for EC2 instances"aws iam create-instance-profile \
--instance-profile-name EC2AccessProfileaws iam add-role-to-instance-profile \
--instance-profile-name EC2AccessProfile \
--role-name EC2AccessRoleaws iam attach-user-policy \
--user-name developer-john \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicyaws iam attach-group-policy \
--group-name backend-developers \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccessaws iam attach-role-policy \
--role-name EC2AccessRole \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicyaws iam attach-role-policy \
--role-name EC2AccessRole \
--policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCoreaws iam list-usersaws iam list-groupsaws iam list-rolesaws iam list-policies --scope Localaws iam list-policies --scope AWSaws iam list-attached-user-policies --user-name developer-johnaws iam list-attached-group-policies --group-name backend-developersaws iam list-attached-role-policies --role-name EC2AccessRoleaws iam get-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicy \
--version-id v1aws iam get-group --group-name backend-developersaws iam list-instance-profilescurl http://169.254.169.254/latest/meta-data/iam/security-credentials/aws iam update-login-profile \
--user-name developer-john \
--password 'NewPassword456!'aws iam create-access-key --user-name developer-johnaws iam delete-access-key \
--user-name developer-john \
--access-key-id AKIAIOSFODNN7EXAMPLEaws iam update-assume-role-policy \
--role-name EC2AccessRole \
--policy-document file://new-trust-policy.jsonaws iam detach-user-policy \
--user-name developer-john \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicyaws iam detach-group-policy \
--group-name backend-developers \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccessaws iam detach-role-policy \
--role-name EC2AccessRole \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicyaws iam remove-user-from-group \
--user-name developer-john \
--group-name backend-developersaws iam delete-access-key \
--user-name developer-john \
--access-key-id AKIAIOSFODNN7EXAMPLEaws iam delete-login-profile --user-name developer-johnaws iam delete-user --user-name developer-johnaws iam delete-group --group-name backend-developersaws iam remove-role-from-instance-profile \
--instance-profile-name EC2AccessProfile \
--role-name EC2AccessRoleaws iam delete-instance-profile \
--instance-profile-name EC2AccessProfileaws iam delete-role --role-name EC2AccessRoleaws iam delete-policy \
--policy-arn arn:aws:iam::123456789012:policy/MyCustomPolicyArchitecture and Flows
IAM Identity and Permission Architecture
EC2 Authentication Flow with IAM Role
IAM User vs IAM Role: Credential Lifecycle
Best Practices
Security
The principle of least privilege is non-negotiable. Every policy should grant the absolute minimum set of permissions required for the task at hand. Using "Action": "*" or "Resource": "*" in production is a critical security liability.
- Never use the root user for day-to-day operations — create dedicated IAM users for all administrative tasks
- Enable MFA on every privileged account without exception
- Rotate access keys on a cadence no longer than 90 days
- Prefer roles over hardcoded keys — for any application running within AWS, roles with temporary credentials are the only defensible approach
- Write specific resource ARNs in policies rather than relying on wildcard resource declarations
- Constrain trust policies to the narrowest possible set of principals
- Audit permissions regularly using IAM Access Analyzer to identify unused or excessive grants
- Never commit credentials to version control under any circumstances
Cost Optimization
- IAM itself is entirely free — there is no charge for users, groups, roles, or policies
- Delete unused resources — orphaned users, roles, and policies create unnecessary attack surface
- Consolidate and reuse policies rather than creating duplicates for each identity
Performance and Reliability
- SDK credential caching — AWS SDKs automatically cache temporary credentials from roles, minimizing metadata service calls
- Maintain multiple admin users — never depend on a single administrative identity
- Version custom policies through infrastructure-as-code tooling such as Terraform or CloudFormation
- Implement Service Control Policies for multi-account organizations managed through AWS Organizations
Operational Excellence
- Adopt strict naming conventions — descriptive names such as
prod-api-ec2-roleordev-s3-access-policymake auditing straightforward - Tag all IAM resources with metadata like
Environment,Team, andProject - Manage IAM through infrastructure as code — Terraform and CloudFormation provide version control, peer review, and repeatability
- Enable CloudTrail to maintain a complete audit trail of every IAM action
- Enforce environment separation — distinct roles and policies for development, staging, and production
Common Mistakes
Cost Considerations
IAM Pricing Summary
| Resource | Cost |
|---|---|
| IAM Users | Free — no limit |
| Groups | Free — no limit |
| Roles | Free — no limit |
| Policies | Free — limit of 10,000 custom policies per account |
| Instance Profiles | Free |
| AssumeRole operations | Free |
| CloudTrail logs for IAM | Only the S3 storage cost for the destination bucket |
Related Indirect Costs
| Service | Pricing |
|---|---|
| CloudTrail | $2 per 100,000 events after the first 5,000 free per month |
| IAM Access Analyzer | $0.20 per 100 findings after the first 10,000 free |
Optimization Strategies
- Remove unused resources — orphaned roles, users, and policies should be deleted as part of regular hygiene
- Consolidate policies — reuse shared policies across identities rather than duplicating definitions
- Disable users rather than deleting when there is a reasonable expectation they may be needed again
IAM is permanently free and fully covered under the AWS Free Tier with no 12-month expiration. There is no limit on standard IAM usage, making it one of the few AWS services with zero direct cost regardless of scale.
Integration with Other Services
| AWS Service | Integration Mechanism | Typical Use Case |
|---|---|---|
| EC2 | Instance Profiles allow EC2 to assume roles | Grant instances access to S3, RDS, and secrets without hardcoded keys |
| Lambda | Execution Role defines function permissions | Allow Lambda to write logs, access DynamoDB, and invoke other functions |
| S3 | Bucket Policies and IAM policies operate in tandem | Control who may read from or write to specific buckets |
| RDS | IAM Database Authentication | Authenticate database connections without static passwords |
| Secrets Manager | IAM policies govern secret access | Regulate access to API keys, passwords, and certificates |
| CloudWatch | IAM policies authorize log and metric writes | EC2 and Lambda send logs using role-derived permissions |
| DynamoDB | Granular table-level IAM policies | Restrict access to read-only, write-only, or specific item conditions |
| STS | Generates temporary credentials for role assumption | Serves as the backend engine for the entire role-based credential system |
| Systems Manager | Session Manager uses IAM roles instead of SSH keys | Connect to EC2 instances without opening SSH ports, authenticating through IAM |
| API Gateway | IAM authorization mode for API endpoints | Require AWS Signature V4 signing on incoming requests |
| CloudFormation | Service Role defines stack-level permissions | Allow CloudFormation to create and manage resources on your behalf |
| Organizations | Service Control Policies at the organizational level | Restrict permissions across entire accounts or organizational units |
| IAM Identity Center | Temporary role assignment through single sign-on | Provide unified login across multiple AWS accounts |
Additional Resources
Official Documentation
Whitepapers
Tools
- IAM Policy Simulator — test policies before applying them
- IAM Access Analyzer — detect excessive permissions
- AWS Policy Generator — generate policies visually