IAM Best Practices
Security recommendations and guidelines for AWS IAM
Follow these best practices to secure your AWS accounts and resources using IAM effectively.
Critical Security
IAM is the foundation of AWS security. Misconfigurations can lead to data breaches, unauthorized access, and significant financial impact.
Root Account Protection
The root account has unrestricted access to all resources. Protect it aggressively.
Enable MFA on Root Account
aws iam get-account-summary --query 'SummaryMap.AccountMFAEnabled'Use a hardware MFA device if possible (YubiKey, etc.).
Never Use Root for Daily Tasks
Create IAM users with appropriate permissions instead.
Delete Root Access Keys
aws iam list-access-keysIf any exist, delete them immediately.
Enable Billing Alerts
Monitor for unexpected charges that might indicate compromised credentials.
Credential Management
Multi-Factor Authentication (MFA)
Enable MFA for all users, especially those with console access or privileged permissions.
aws iam generate-credential-report
aws iam get-credential-report --query 'Content' --output text | base64 -d | grep 'false' | cut -d',' -f1Access Key Rotation
Rotate access keys regularly (every 90 days is common):
aws iam list-access-keys --user-name developer --query 'AccessKeyMetadata[?CreateDate<`2024-01-01`]'Create New Key
aws iam create-access-key --user-name developerUpdate Applications
Deploy new credentials to all applications.
Deactivate Old Key
aws iam update-access-key --user-name developer --access-key-id OLD_KEY --status InactiveDelete Old Key
After confirming everything works:
aws iam delete-access-key --user-name developer --access-key-id OLD_KEYPassword Policy
Enforce strong passwords account-wide:
aws iam update-account-password-policy \
--minimum-password-length 14 \
--require-symbols \
--require-numbers \
--require-uppercase-characters \
--require-lowercase-characters \
--allow-users-to-change-password \
--max-password-age 90 \
--password-reuse-prevention 24 \
--hard-expiryLeast Privilege Principle
Grant only the permissions necessary to perform a task.
Start Restrictive
Begin with no permissions and add as needed:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-bucket/specific-path/*"
}
]
}Use Access Analyzer
Identify unused permissions:
aws accessanalyzer start-policy-generation \
--policy-generation-details '{
"principalArn": "arn:aws:iam::123456789012:role/MyRole"
}'Avoid Wildcards
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::my-bucket/uploads/*"
}Use Roles, Not Users
For AWS Services
Never embed credentials in code. Use IAM roles:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}For Cross-Account Access
Use role assumption instead of sharing credentials:
aws sts assume-role \
--role-arn arn:aws:iam::TARGET:role/CrossAccountRole \
--role-session-name my-sessionFor EC2 Instances
Attach instance profiles instead of storing credentials:
aws ec2 associate-iam-instance-profile \
--instance-id i-1234567890abcdef0 \
--iam-instance-profile Name=MyInstanceProfileUse Groups for Permissions
Organize users into groups and attach policies to groups:
# Create group
aws iam create-group --group-name Developers
# Attach policy to group
aws iam attach-group-policy \
--group-name Developers \
--policy-arn arn:aws:iam::aws:policy/PowerUserAccess
# Add user to group
aws iam add-user-to-group \
--user-name developer \
--group-name DevelopersBenefits:
- Easier to manage permissions at scale
- Clear audit trail
- Consistent permissions for similar roles
Use Permission Boundaries
Limit the maximum permissions users/roles can have:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"dynamodb:*",
"lambda:*",
"logs:*"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"iam:*",
"organizations:*",
"account:*"
],
"Resource": "*"
}
]
}aws iam put-user-permissions-boundary \
--user-name developer \
--permissions-boundary arn:aws:iam::123456789012:policy/DeveloperBoundaryMonitoring and Auditing
Enable CloudTrail
Log all API calls for security analysis:
aws cloudtrail create-trail \
--name security-trail \
--s3-bucket-name my-cloudtrail-bucket \
--is-multi-region-trail \
--enable-log-file-validationMonitor with CloudWatch
Create alarms for suspicious activity:
aws cloudwatch put-metric-alarm \
--alarm-name RootAccountUsage \
--metric-name RootAccountUsageCount \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alertsRegular Credential Reports
Generate and review credential reports:
aws iam generate-credential-report
aws iam get-credential-report --output text --query Content | base64 -d > report.csvCheck for:
- Users without MFA
- Unused credentials
- Old access keys
- Root account usage
Secure Conditions
Add conditions to policies for extra security:
Require MFA
{
"Condition": {
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}Restrict by IP
{
"Condition": {
"IpAddress": {
"aws:SourceIp": ["10.0.0.0/8"]
}
}
}Require HTTPS
{
"Condition": {
"Bool": {
"aws:SecureTransport": "true"
}
}
}Time-Based Access
{
"Condition": {
"DateGreaterThan": {"aws:CurrentTime": "2024-01-01T08:00:00Z"},
"DateLessThan": {"aws:CurrentTime": "2024-01-01T18:00:00Z"}
}
}Security Checklist
IAM Security Checklist
- Root account has MFA enabled
- Root account has no access keys
- All users have MFA enabled
- Strong password policy is configured
- Access keys are rotated regularly (< 90 days)
- Unused users and roles are removed
- Least privilege is applied
- Roles are used instead of long-term credentials
- CloudTrail is enabled
- Access Analyzer is configured
- Regular credential report reviews