Skip to main content
AWSSecurityintermediate

AWS IAM Best Practices

Essential IAM security practices including least privilege, MFA, and role-based access patterns.

CloudToolStack Team25 min readPublished Feb 22, 2026

Prerequisites

Why IAM Security Matters

AWS Identity and Access Management (IAM) is the backbone of your cloud security posture. Every single API call in AWS, whether from the console, CLI, SDK, or an automated service, is authenticated and authorized through IAM. This makes IAM the single most important service to configure correctly. A misconfigured IAM policy can expose your entire AWS environment to unauthorized access, data exfiltration, or complete account takeover.

According to multiple industry reports, IAM misconfigurations consistently rank among the top causes of cloud security breaches. The challenge is that IAM is both incredibly powerful and deeply complex. AWS has over 300 services, each with dozens of API actions, creating a permission surface area of thousands of possible action-resource combinations. Without a disciplined approach to IAM, teams inevitably drift toward overly permissive policies that violate the principle of least privilege.

This guide covers battle-tested IAM best practices that every team should implement, from individual developer accounts to large enterprise deployments. Whether you are securing a single-account startup or a multi-account enterprise with hundreds of workloads, these principles will help you build a robust security foundation.

Root Account Alert

Never use the AWS root account for day-to-day operations. The root account has unrestricted access to every resource in the account and cannot be limited by IAM policies, SCPs, or permission boundaries. Enable MFA on the root account immediately after creating it, then lock away the credentials in a secure location like a hardware security module or a physical safe. Only use the root account for the handful of tasks that require it, such as changing your account support plan or closing the account.

Principle of Least Privilege

The principle of least privilege means granting only the permissions required to perform a task and nothing more. This is the single most impactful IAM practice you can adopt. The reason is simple: every additional permission granted to a principal is a potential attack vector. If a Lambda function only needs to read from a specific S3 bucket, it should not have s3:* on *. If an EC2 instance only writes logs to CloudWatch, it should not have the CloudWatchFullAccess managed policy.

The practical challenge is that starting with zero permissions and adding them incrementally requires more effort upfront. Many teams default to broad policies likeAdministratorAccess or PowerUserAccess during development and never revisit them. This creates significant security risk in production. The correct approach is to start with zero permissions and add them as needed, using the tools AWS provides to right-size policies over time.

Tools for Right-Sizing Permissions

AWS provides several tools to help you implement and maintain least privilege:

  • IAM Access Analyzer Policy Generation: Analyzes CloudTrail logs to generate a fine-grained policy based on the actual API calls made by a principal over a specified period. This is the most effective way to create least-privilege policies for existing workloads.
  • IAM Access Advisor: Shows the last time each AWS service was accessed by a principal. If a role has S3 permissions but has not accessed S3 in 90 days, those permissions are candidates for removal.
  • IAM Policy Simulator: Lets you test policies against specific API actions and resources before applying them in production. Use it to verify that a new policy grants exactly the permissions you intend.
  • CloudTrail Lake: Query your CloudTrail events using SQL to identify which API actions a principal actually uses. This provides more granular insights than Access Advisor.
  • IAM Access Analyzer Unused Access: Identifies unused roles, unused access keys, and unused permissions across your accounts. Available with IAM Access Analyzer at no additional cost.
least-privilege-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadSpecificBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-data-bucket",
        "arn:aws:s3:::my-app-data-bucket/*"
      ]
    }
  ]
}

Common Least-Privilege Anti-Patterns

Avoid these common mistakes that undermine least privilege:

Anti-PatternRiskCorrect Approach
"Action": "*"Grants all actions on all servicesSpecify exact actions needed
"Resource": "*"Applies to every resource in the accountScope to specific ARNs or patterns
Using AWS managed FullAccess policiesGrants far more permissions than neededCreate custom policies or use Access Analyzer
Sharing credentials between servicesNo blast radius isolationOne IAM role per service or function
Not reviewing permissions after initial setupPermission drift over timeQuarterly reviews using Access Advisor

Use Conditions for Extra Security

Add condition keys to further restrict when a policy applies. For example, restrict access to specific IP ranges with aws:SourceIp, require MFA withaws:MultiFactorAuthPresent, limit actions to specific regions withaws:RequestedRegion, or restrict to specific VPCs withaws:SourceVpc. Condition keys are one of the most powerful and underused features of IAM policies.

policy-with-conditions.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowEC2WithMFAAndRegion",
      "Effect": "Allow",
      "Action": [
        "ec2:StartInstances",
        "ec2:StopInstances",
        "ec2:TerminateInstances"
      ],
      "Resource": "arn:aws:ec2:us-east-1:123456789012:instance/*",
      "Condition": {
        "Bool": {
          "aws:MultiFactorAuthPresent": "true"
        },
        "StringEquals": {
          "aws:RequestedRegion": "us-east-1",
          "ec2:ResourceTag/Environment": "development"
        }
      }
    }
  ]
}

IAM Roles Over Long-Lived Credentials

IAM roles provide temporary security credentials that automatically rotate, eliminating the risk of leaked long-lived access keys. Wherever possible, use roles instead of IAM users with access keys. This is not just a best practice; it is a fundamental security principle. Long-lived credentials are the most common vector for cloud security breaches because they can be accidentally committed to source code, stored in insecure locations, or exfiltrated from compromised systems.

Every AWS compute service supports IAM roles natively. EC2 instances use instance profiles, Lambda functions have execution roles, ECS tasks have task roles, and EKS pods can use IAM Roles for Service Accounts (IRSA) or EKS Pod Identity. There is almost no legitimate reason to use long-lived access keys for workloads running in AWS.

Credential TypeRotationRisk LevelUse Case
IAM Role (STS)Automatic (1-12 hours)LowEC2, Lambda, ECS, cross-account
IAM User Access KeyManualHighLegacy systems, third-party tools
SSO / Identity CenterSession-basedLowHuman user access to AWS Console/CLI
OIDC FederationToken-based (minutes)LowCI/CD (GitHub Actions, GitLab CI)

EC2 Instance Profiles

An instance profile is a container for an IAM role that allows EC2 instances to assume that role. When you associate an instance profile with an EC2 instance, any application running on that instance can retrieve temporary credentials from the instance metadata service (IMDS). The AWS SDKs automatically discover and use these credentials, so your application code does not need to handle credential management at all.

ec2-trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Enforce IMDSv2

The EC2 Instance Metadata Service v1 (IMDSv1) is vulnerable to Server-Side Request Forgery (SSRF) attacks. An attacker who can make HTTP requests from your instance can steal the IAM role credentials. Always enforce IMDSv2, which requires a session token and is not vulnerable to SSRF. Set HttpTokens: required in your launch template or use an SCP to deny ec2:RunInstances without IMDSv2.

enforce-imdsv2-scp.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EnforceIMDSv2",
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotEquals": {
          "ec2:MetadataHttpTokens": "required"
        }
      }
    }
  ]
}

Cross-Account Access with Roles

For cross-account access, use role chaining with an external ID to prevent the confused deputy problem. The confused deputy is a security issue where a trusted service (such as a third-party SaaS tool) could be tricked into assuming a role in your account on behalf of an unauthorized actor. The external ID serves as a shared secret between you and the trusted third party, ensuring that only the intended principal can assume the role.

cross-account-trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::987654321098:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "unique-external-id-from-partner"
        }
      }
    }
  ]
}

CI/CD with OIDC Federation

Modern CI/CD platforms like GitHub Actions, GitLab CI, and CircleCI support OIDC federation, which allows your pipelines to assume IAM roles without storing any AWS credentials. This is the most secure approach for CI/CD because there are no secrets to manage, rotate, or accidentally expose.

github-actions-oidc.yaml
# CloudFormation for GitHub Actions OIDC
GitHubOIDCProvider:
  Type: AWS::IAM::OIDCProvider
  Properties:
    Url: https://token.actions.githubusercontent.com
    ClientIdList:
      - sts.amazonaws.com
    ThumbprintList:
      - 6938fd4d98bab03faadb97b34396831e3780aea1

GitHubActionsRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: github-actions-deploy
    AssumeRolePolicyDocument:
      Version: '2012-10-17'
      Statement:
        - Effect: Allow
          Principal:
            Federated: !Ref GitHubOIDCProvider
          Action: sts:AssumeRoleWithWebIdentity
          Condition:
            StringEquals:
              token.actions.githubusercontent.com:aud: sts.amazonaws.com
            StringLike:
              token.actions.githubusercontent.com:sub: repo:my-org/my-repo:ref:refs/heads/main
AWS Security Hub Overview: Centralized Security Findings

Permission Boundaries

Permission boundaries set the maximum permissions that an IAM entity can have. They are essential in environments where you delegate IAM administration to developers or teams. Even if a developer creates a role with AdministratorAccess, the permission boundary ensures the effective permissions never exceed the boundary. This is the key mechanism for safely delegating IAM capabilities without risking privilege escalation.

Think of permission boundaries as guardrails on a highway. The identity-based policy determines where you want to go (what permissions you request), and the permission boundary determines where you are allowed to go (the maximum possible permissions). The effective permissions are the intersection of both policies. A request is only allowed if it is permitted by both the identity-based policy and the permission boundary.

How Permission Boundaries Work

When you attach a permission boundary to an IAM user or role, it acts as a ceiling on the permissions that entity can have. The permission boundary does not grant any permissions by itself. It only limits the maximum permissions that can be granted by identity-based policies.

permission-boundary.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowedServices",
      "Effect": "Allow",
      "Action": [
        "s3:*",
        "dynamodb:*",
        "lambda:*",
        "logs:*",
        "sqs:*",
        "sns:*",
        "cloudwatch:*",
        "xray:*",
        "apigateway:*"
      ],
      "Resource": "*"
    },
    {
      "Sid": "AllowIAMWithBoundary",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:AttachRolePolicy",
        "iam:PutRolePolicy",
        "iam:DeleteRole",
        "iam:DetachRolePolicy",
        "iam:DeleteRolePolicy",
        "iam:PutRolePermissionsBoundary"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "iam:PermissionsBoundary": "arn:aws:iam::123456789012:policy/DevBoundary"
        }
      }
    },
    {
      "Sid": "DenyBoundaryRemoval",
      "Effect": "Deny",
      "Action": [
        "iam:DeleteRolePermissionsBoundary",
        "iam:DeleteUserPermissionsBoundary"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyEscalation",
      "Effect": "Deny",
      "Action": [
        "iam:CreateUser",
        "organizations:*",
        "account:*"
      ],
      "Resource": "*"
    }
  ]
}

Understanding Effective Permissions

The effective permissions for a principal are the intersection of identity-based policies, resource-based policies, permission boundaries, SCPs, and session policies. A permission must be allowed by all applicable policy types and not explicitly denied by any. An explicit Deny in any policy always wins, regardless of any Allow statements in other policies. This evaluation logic is the foundation of AWS IAM security, and understanding it is critical for troubleshooting access issues.

Permission Boundary Use Cases

Use CaseDescriptionBoundary Strategy
Developer self-serviceDevelopers create their own Lambda rolesAllow serverless services, deny IAM escalation
Team autonomyEach team manages their own resourcesAllow team-specific services, enforce tagging
Contractor accessTemporary workers need limited AWS accessAllow only specific services and regions
Sandbox accountsExperimentation without riskAllow broad services, deny networking and IAM changes

Multi-Account Strategy with SCPs

AWS Organizations Service Control Policies (SCPs) provide guardrails across your entire organization. SCPs do not grant permissions; they restrict what is allowed. They act as the outermost boundary on what any principal in an account can do, regardless of what IAM policies are attached. This makes SCPs the most powerful governance tool in AWS.

In a well-architected multi-account environment, you separate workloads into dedicated accounts for blast radius isolation, billing separation, and simplified governance. SCPs applied to organizational units (OUs) ensure that security guardrails are consistently enforced across all accounts in that OU, even if individual account administrators try to circumvent them.

Common SCP Patterns

The following SCP patterns are recommended for most organizations:

  • Prevent disabling CloudTrail: Ensures a complete audit trail is always maintained in every account
  • Prevent disabling GuardDuty: Maintains continuous threat detection coverage
  • Restrict AWS regions: Prevents resource creation in unapproved regions, reducing attack surface
  • Prevent creation of IAM users: Forces use of IAM Identity Center for human access
  • Require encryption: Denies creation of unencrypted S3 buckets, EBS volumes, and RDS instances
  • Protect security services: Prevents disabling Security Hub, Config, or Access Analyzer
  • Deny root account actions: Prevents the root user from performing API actions in member accounts
restrict-regions-scp.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyNonApprovedRegions",
      "Effect": "Deny",
      "NotAction": [
        "iam:*",
        "organizations:*",
        "sts:*",
        "support:*",
        "budgets:*",
        "cloudfront:*",
        "route53:*",
        "waf:*",
        "wafv2:*",
        "cloudwatch:GetMetricData",
        "cloudwatch:ListMetrics"
      ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "us-east-1",
            "us-west-2",
            "eu-west-1"
          ]
        }
      }
    }
  ]
}
prevent-security-disable-scp.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PreventCloudTrailDisable",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:StopLogging",
        "cloudtrail:DeleteTrail",
        "cloudtrail:UpdateTrail"
      ],
      "Resource": "arn:aws:cloudtrail:*:*:trail/organization-trail"
    },
    {
      "Sid": "PreventGuardDutyDisable",
      "Effect": "Deny",
      "Action": [
        "guardduty:DeleteDetector",
        "guardduty:DisassociateFromMasterAccount",
        "guardduty:UpdateDetector"
      ],
      "Resource": "*"
    },
    {
      "Sid": "PreventSecurityHubDisable",
      "Effect": "Deny",
      "Action": [
        "securityhub:DisableSecurityHub",
        "securityhub:DisassociateFromMasterAccount"
      ],
      "Resource": "*"
    }
  ]
}

SCP Testing Is Critical

SCPs can break workloads if applied too broadly. Always test SCPs in a sandbox OU before applying them to production. Use the NotAction element carefully, as it allows all actions except those listed. Monitor AWS CloudTrail forAccessDenied events after applying SCPs to catch unintended blocks. Remember that SCPs do not affect the management account, only member accounts.

AWS Well-Architected Overview: Security Pillar Deep Dive

IAM Identity Center (AWS SSO)

IAM Identity Center (formerly AWS SSO) is the recommended way for human users to access AWS accounts and applications. It provides centralized identity management, temporary credentials, and a single sign-on experience across all accounts in your organization. This eliminates the need for IAM users with long-lived passwords and access keys.

Key Benefits of Identity Center

  • Centralized user management: Manage users in one place, either in the built-in directory or federated from an external IdP like Okta, Azure AD, or Google Workspace
  • Temporary credentials: Users receive short-lived credentials for each session, eliminating the risk of credential leakage
  • Permission sets: Define reusable sets of permissions that can be assigned to users and groups across multiple accounts
  • MFA enforcement: Require MFA for all users accessing the SSO portal, providing a single enforcement point
  • CLI integration: The AWS CLI v2 natively supports SSO, allowing developers to authenticate with aws sso login
aws-sso-cli-config
# Configure AWS CLI for SSO
aws configure sso
# Follow the prompts to set up SSO profile

# Login with SSO
aws sso login --profile my-sso-profile

# Use the SSO profile for commands
aws s3 ls --profile my-sso-profile

# Example ~/.aws/config entry
# [profile dev-account]
# sso_session = my-org
# sso_account_id = 123456789012
# sso_role_name = DeveloperAccess
# region = us-east-1
#
# [sso-session my-org]
# sso_start_url = https://my-org.awsapps.com/start
# sso_region = us-east-1
# sso_registration_scopes = sso:account:access

Permission Sets Strategy

Permission sets define what users can do in each account. Create a hierarchy of permission sets that align with your organization's roles:

Permission SetPurposeAttached Policies
AdministratorAccessAccount owners, break-glassAWS managed AdministratorAccess
DeveloperAccessDay-to-day development workCustom policy with service restrictions
ReadOnlyAccessAuditing, troubleshootingAWS managed ReadOnlyAccess
DatabaseAdminDatabase operations teamCustom policy for RDS, DynamoDB, ElastiCache
SecurityAuditSecurity team reviewAWS managed SecurityAudit + custom policies
Multi-Cloud Identity Federation Guide

MFA and Password Policies

Multi-Factor Authentication (MFA) adds a critical second layer of authentication beyond passwords. In AWS, MFA should be required for all human user access, especially for high-privilege operations. Even if credentials are compromised, MFA prevents unauthorized access unless the attacker also possesses the MFA device.

MFA Device Types

Device TypeSecurity LevelUse Case
FIDO2 security key (YubiKey)Highest, phishing-resistantRoot account, admin users
Hardware TOTP tokenHigh, no software dependencyRoot account (recommended backup)
Virtual MFA (authenticator app)Medium, software-basedStandard user accounts

Enforcing MFA with IAM Policies

You can create an IAM policy that denies all actions except IAM self-service until the user has authenticated with MFA. This is commonly called the “force MFA” policy pattern.

force-mfa-policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSelfManagementWithoutMFA",
      "Effect": "Allow",
      "Action": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:GetUser",
        "iam:ListMFADevices",
        "iam:ListVirtualMFADevices",
        "iam:ResyncMFADevice",
        "iam:ChangePassword",
        "sts:GetSessionToken"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyAllExceptSelfManagementWithoutMFA",
      "Effect": "Deny",
      "NotAction": [
        "iam:CreateVirtualMFADevice",
        "iam:EnableMFADevice",
        "iam:GetUser",
        "iam:ListMFADevices",
        "iam:ListVirtualMFADevices",
        "iam:ResyncMFADevice",
        "iam:ChangePassword",
        "sts:GetSessionToken"
      ],
      "Resource": "*",
      "Condition": {
        "BoolIfExists": {
          "aws:MultiFactorAuthPresent": "false"
        }
      }
    }
  ]
}

Resource-Based Policies

Resource-based policies are JSON policies attached directly to AWS resources such as S3 buckets, SQS queues, KMS keys, Lambda functions, and SNS topics. Unlike identity-based policies which are attached to users, groups, or roles, resource-based policies define who can access the resource, including principals from other AWS accounts.

Resource-based policies are unique because they can grant cross-account access without requiring the caller to assume a role. When a principal in Account A accesses a resource with a resource-based policy in Account B, the principal uses its own permissions combined with the resource policy, and there is no need to assume a role in Account B.

Resource Policy Best Practices

  • Avoid wildcards in Principal: Never use "Principal": "*" without conditions. This grants access to anyone, including anonymous users.
  • Use conditions for cross-account access: Add aws:PrincipalOrgID to restrict access to principals within your AWS organization.
  • Audit with Access Analyzer: IAM Access Analyzer automatically identifies resource-based policies that grant access to external entities.
  • Encrypt with KMS: Use KMS key policies to control who can encrypt and decrypt data, providing an additional layer of access control beyond the resource policy.
s3-bucket-policy-org.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowOrgAccess",
      "Effect": "Allow",
      "Principal": "*",
      "Action": [
        "s3:GetObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::shared-data-bucket",
        "arn:aws:s3:::shared-data-bucket/*"
      ],
      "Condition": {
        "StringEquals": {
          "aws:PrincipalOrgID": "o-abc123def456"
        }
      }
    },
    {
      "Sid": "DenyUnencryptedTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::shared-data-bucket",
        "arn:aws:s3:::shared-data-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}
AWS S3 Storage Classes: Understanding S3 Security and Access Tiers

Auditing and Monitoring

IAM security is not a set-and-forget exercise. Continuous monitoring and regular audits are essential to maintain a strong security posture over time. Threats evolve, team members change, and permissions accumulate unless actively managed. Implement the following monitoring and audit practices to detect issues early and respond quickly.

CloudTrail for Complete Audit Trail

Enable CloudTrail in all regions and all accounts. Every IAM API call is logged, providing a complete audit trail of who did what, when, and from where. Use a centralized CloudTrail configuration through AWS Organizations to ensure consistent logging across your entire environment.

  • Organization trail: Creates a trail in every account automatically, logged to a central S3 bucket in the log archive account
  • CloudTrail Lake: Enables SQL-based querying of CloudTrail events for forensic investigation and trend analysis
  • Data events: Enable data event logging for S3 and Lambda to track object-level and invocation-level activity
  • Insights events: CloudTrail Insights automatically detects unusual API activity patterns

IAM Access Analyzer

Use IAM Access Analyzer to detect resources shared with external entities, such as S3 buckets with public access, KMS keys with cross-account grants, or IAM roles with overly broad trust policies. Access Analyzer continuously monitors your environment and generates findings when it detects external access.

Credential Reports and Security Alerts

Generate credential reports regularly to identify users with old access keys, users without MFA, and inactive accounts. Set up CloudWatch alarms for sensitive IAM events to detect potential security incidents in real time.

cloudwatch-iam-alarms.json
{
  "Type": "AWS::CloudWatch::Alarm",
  "Properties": {
    "AlarmName": "RootAccountUsage",
    "MetricName": "RootAccountUsageCount",
    "Namespace": "CloudTrailMetrics",
    "Statistic": "Sum",
    "Period": 300,
    "EvaluationPeriods": 1,
    "Threshold": 1,
    "ComparisonOperator": "GreaterThanOrEqualToThreshold",
    "AlarmActions": [
      "arn:aws:sns:us-east-1:123456789012:SecurityAlerts"
    ]
  }
}
generate-credential-report.sh
# Generate and download IAM credential report
aws iam generate-credential-report
sleep 10
aws iam get-credential-report \
  --query 'Content' \
  --output text | base64 --decode > credential-report.csv

# Find users without MFA enabled
awk -F',' 'NR>1 && $4=="true" && $8=="false" {print $1, "- Password enabled but no MFA"}' \
  credential-report.csv

# Find access keys older than 90 days
awk -F',' 'NR>1 && $9=="true" {
  cmd="date -d \"" $10 "\" +%s 2>/dev/null || date -j -f \"%Y-%m-%dT%H:%M:%S+00:00\" \"" $10 "\" +%s"
  cmd | getline created
  close(cmd)
  now=systime()
  age=(now-created)/86400
  if (age > 90) print $1, "- Access key 1 is", int(age), "days old"
}' credential-report.csv

Automated Compliance with AWS Config

Use AWS Config rules to continuously monitor IAM compliance. Key rules includeiam-root-access-key-check (no root access keys),iam-user-mfa-enabled (MFA on all users),iam-user-unused-credentials-check (no stale credentials), andiam-policy-no-statements-with-admin-access (no admin policies on users). Config rules trigger automatically when resources change, providing continuous compliance monitoring.

AWS Security Hub: Aggregate IAM Findings Across Accounts

IAM Policy Structure and Evaluation Logic

Understanding how AWS evaluates IAM policies is essential for writing effective policies and troubleshooting access issues. AWS follows a specific evaluation order when a principal makes a request:

  1. Explicit Deny: If any applicable policy contains an explicit Deny, the request is denied immediately. This takes precedence over everything.
  2. SCPs: If the account is part of an AWS Organization, SCPs must allow the action. SCPs do not apply to the management account.
  3. Resource-based policies: If a resource-based policy grants access, the request may be allowed even without an identity-based policy (for same-account access).
  4. Permission boundaries: If a permission boundary is attached, it must allow the action.
  5. Session policies: For assumed roles with session policies, the session policy must allow the action.
  6. Identity-based policies: Finally, the identity-based policies attached to the principal must allow the action.

The default is Deny. Unless an explicit Allow exists in the appropriate policies (and no Deny blocks it), the request is denied.

Policy Elements Reference

ElementPurposeExample
EffectAllow or Deny"Effect": "Allow"
ActionAPI actions to allow/deny"Action": "s3:GetObject"
NotActionAll actions except those listed"NotAction": "iam:*"
ResourceARNs the statement applies to"Resource": "arn:aws:s3:::my-bucket/*"
ConditionWhen the statement appliesIP ranges, MFA, tags, regions
PrincipalWho the policy applies to (resource policies)Account IDs, service principals

IAM for Specific AWS Services

Different AWS services have unique IAM integration patterns. Understanding these patterns helps you implement least privilege correctly for each service.

Lambda Execution Roles

Every Lambda function needs an execution role that grants permission to write logs to CloudWatch and access any other AWS services the function uses. Use theAWSLambdaBasicExecutionRole managed policy as a starting point for CloudWatch Logs access, then add only the specific permissions your function needs.

ECS Task Roles vs Execution Roles

ECS distinguishes between task execution roles (used by the ECS agent to pull images and write logs) and task roles (used by your application code to access AWS services). Keep these separate and scope each to its specific purpose.

EKS Pod Identity

EKS Pod Identity (the successor to IRSA) provides fine-grained IAM roles for individual Kubernetes pods. Each pod can assume a different IAM role, enabling least-privilege access at the pod level rather than the node level.

AWS Lambda Performance Tuning: Execution Roles and SecurityECS vs EKS Decision Guide: IAM Integration Patterns

IAM Security Automation

Manual IAM management does not scale. As your organization grows, automate IAM governance using Infrastructure as Code, automated remediation, and continuous compliance monitoring.

Infrastructure as Code for IAM

Define all IAM resources (roles, policies, groups, permission boundaries) in CloudFormation, CDK, or Terraform. Never create IAM resources manually through the console. Code-defined IAM enables version control, peer review, and automated testing of permission changes before they reach production.

iam-cdk-example.ts
import * as iam from 'aws-cdk-lib/aws-iam';
import * as cdk from 'aws-cdk-lib';

// Create a role with a permission boundary
const devRole = new iam.Role(this, 'DeveloperRole', {
  assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
  permissionsBoundary: iam.ManagedPolicy.fromManagedPolicyName(
    this, 'Boundary', 'DevBoundary'
  ),
});

// Grant least-privilege access
const bucket = s3.Bucket.fromBucketName(this, 'DataBucket', 'my-data');
bucket.grantRead(devRole);

// Use CDK Nag to enforce IAM best practices
import { AwsSolutionsChecks } from 'cdk-nag';
cdk.Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));

Automated Remediation

Use EventBridge rules to detect and automatically remediate IAM security issues:

  • Automatically deactivate access keys older than 90 days
  • Remove inline policies from IAM users (force use of managed policies)
  • Detach overly permissive policies (like AdministratorAccess) from non-admin roles
  • Alert on any IAM policy change in production accounts
CloudFormation vs CDK: IaC for IAM GovernanceMulti-Cloud IAM Rosetta Stone: Compare IAM Across Providers

IAM Troubleshooting Checklist

When a principal receives an AccessDenied error, work through this checklist systematically:

  1. Check identity-based policies: Does the principal have a policy that allows the action on the resource?
  2. Check resource-based policies: Does the resource have a policy that allows or denies the principal?
  3. Check permission boundaries: Is there a permission boundary that restricts the principal?
  4. Check SCPs: Is there an SCP on the account or OU that denies the action?
  5. Check session policies: If the principal assumed a role, was a session policy applied that restricts the action?
  6. Check the region: Some services (like S3 bucket creation) are region-specific. Ensure the policy applies to the correct region.
  7. Check conditions: Are there condition keys that are not being met (MFA, source IP, tags)?
  8. Use CloudTrail: Find the specific API call in CloudTrail. The error message often includes which policy denied access.
  9. Use Policy Simulator: Test the specific action with the IAM Policy Simulator to identify which policy is causing the denial.

Key Takeaways

Start with least privilege and use Access Analyzer to right-size permissions over time. Prefer IAM roles with temporary credentials over long-lived access keys in all cases. Use permission boundaries to safely delegate IAM administration to teams. Enforce organization-wide guardrails with SCPs. Centralize human access through IAM Identity Center with MFA required. Define all IAM resources in Infrastructure as Code for auditability and peer review. Monitor continuously with CloudTrail, Access Analyzer, and Config rules. IAM security is a continuous process, not a one-time setup. Invest in automation to maintain your security posture as your organization scales.

AWS Cost Optimization: Governance and Tagging Strategies

Key Takeaways

  1. 1Never use the root account for day-to-day operations; enable MFA immediately.
  2. 2Apply the principle of least privilege using IAM Access Analyzer and policy generation.
  3. 3Use IAM roles with temporary credentials instead of long-lived access keys.
  4. 4Permission boundaries enable safe delegation of IAM administration.
  5. 5Service Control Policies enforce guardrails across your entire organization.
  6. 6Continuously audit with CloudTrail, Access Analyzer, and credential reports.

Frequently Asked Questions

What is the principle of least privilege in AWS IAM?
Least privilege means granting only the minimum permissions required to perform a task. Start with zero permissions and add them incrementally. Use IAM Access Analyzer to generate policies based on actual CloudTrail access activity.
Should I use IAM users or IAM roles?
Prefer IAM roles with temporary credentials whenever possible. Roles auto-rotate credentials and eliminate the risk of leaked access keys. Use IAM Identity Center (SSO) for human users and IAM roles for applications and services.
What are AWS permission boundaries?
Permission boundaries set the maximum permissions an IAM entity can have. Even if a role has AdministratorAccess, the effective permissions are the intersection of the identity policy and the boundary. They enable safe delegation of IAM administration.
How do I audit IAM permissions in AWS?
Use IAM Access Analyzer for external access findings, Access Advisor for last-accessed timestamps, credential reports for key age and MFA status, and CloudTrail for a complete API audit trail. Set up CloudWatch alarms for sensitive IAM events.
What is the difference between SCPs and IAM policies?
IAM policies grant permissions to principals. SCPs restrict what is allowed within an AWS Organizations account and never grant permissions. SCPs act as guardrails that limit the maximum available permissions for all principals in an account.
How often should I rotate AWS access keys?
AWS recommends rotating access keys every 90 days. Better yet, eliminate long-lived keys entirely by using IAM roles, Identity Center, or OIDC federation. Use credential reports to identify keys that need rotation.

Written by CloudToolStack Team

Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.

Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.