Introduction
AWS Identity and Access Management (IAM) is the control plane for your entire AWS environment. Every API call, every service interaction, and every human action passes through IAM. When IAM is misconfigured, the blast radius is not a single resource — it is your entire cloud estate.
The IAM threat landscape has shifted significantly. Attacks are no longer limited to stolen root credentials or missing MFA. Modern breaches exploit overly permissive policies that grant far more access than needed, cross-account trust relationships that create unintended backdoors, and accumulated unused permissions that expand the attack surface silently over time.
According to AWS, IAM Access Analyzer findings reveal that most organizations have policies granting external or unused access they are completely unaware of. The gap between what is permitted and what is actually used is where attackers operate.
This article covers the most critical AWS IAM misconfigurations that security teams encounter today — focused on the architectural and policy-level mistakes that lead to real breaches, not just checkbox compliance items.
1. Overly Permissive IAM Policies
The most pervasive IAM misconfiguration is granting more permissions than necessary. This takes several forms: wildcard actions ("Action": "*"), wildcard resources ("Resource": "*"), or broad service-level permissions like s3:* or ec2:* when only specific actions are needed.
Why it matters: An overly permissive policy on a compromised role gives an attacker immediate lateral movement capability. If a Lambda function has s3:* on all resources but only needs s3:GetObject on one bucket, a compromise of that function exposes every S3 bucket in the account.
The pattern that causes this: Teams use broad permissions during development for speed, then never scope them down before production. AWS managed policies like AdministratorAccess or PowerUserAccess get attached to service roles because they “just work.”
How to Fix
Use IAM Access Analyzer to generate least-privilege policies based on actual usage:
aws accessanalyzer start-policy-generation --policy-generation-details \
'{"principalArn":"arn:aws:iam::123456789012:role/my-service-role"}'
This generates a policy based on the CloudTrail activity of the role over a specified period — only the actions and resources actually used are included.
Review policies for wildcards:
aws iam list-policies --scope Local --query 'Policies[].Arn' --output text | \
xargs -I {} aws iam get-policy-version --policy-arn {} \
--version-id $(aws iam get-policy --policy-arn {} --query 'Policy.DefaultVersionId' --output text)
Best practice: Start with zero permissions and add only what is needed. Use IAM policy conditions to restrict access by source IP, VPC, time of day, or MFA status. Implement service-specific conditions like s3:prefix to limit object-level access.
2. Unused Permissions and Dormant Access
Permissions that exist but are never used represent unnecessary attack surface. This includes IAM roles that have not been assumed in months, access keys that have never been rotated, users with console passwords but no recent login, and policies granting actions that are never invoked.
Why it matters: Unused permissions are invisible privilege escalation paths. An attacker who compromises any identity with dormant but valid permissions can use those permissions immediately — and the activity will not trigger usage-based anomaly detection because there is no baseline to compare against.
Scale of the problem: AWS IAM Access Analyzer’s unused access findings typically surface hundreds of unused permissions per account. Most organizations have roles created for projects that ended years ago, still sitting with active permissions.
How to Fix
Enable the IAM Access Analyzer unused access analyzer at the organization level:
aws accessanalyzer create-analyzer \
--analyzer-name org-unused-access \
--type ORGANIZATION_UNUSED_ACCESS \
--configuration '{"unusedAccess":{"unusedAccessAge":90}}'
This continuously monitors all IAM roles and users, flagging unused roles, unused access keys, unused passwords, and unused service-level permissions.
Review and act on findings:
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/org-unused-access \
--filter '{"findingType":{"eq":["UnusedPermission"]}}'
Identify roles that have never been assumed:
aws iam list-roles --query 'Roles[?RoleLastUsed.LastUsedDate==`null`].{RoleName:RoleName,Created:CreateDate}'
Identify stale access keys:
aws iam generate-credential-report
aws iam get-credential-report --query 'Content' --output text | base64 -d | \
awk -F',' '$11!="N/A" && $12=="false" {print $1, $11}'
Best practice: Set up a quarterly review cycle. Any IAM role unused for 90 days should be quarantined (denied all actions via an inline deny policy) for 30 days, then deleted if no one raises an issue.
3. Cross-Account Role Trust Policy Misconfiguration
Cross-account IAM roles allow one AWS account to assume a role in another. The trust policy on the role defines who can assume it. Misconfigurations here create direct paths for unauthorized access — sometimes from entirely external AWS accounts.
Why it matters: A misconfigured trust policy can allow any principal in another AWS account to assume a privileged role in your account. If the trust policy specifies only an account ID as the principal (e.g., "Principal": {"AWS": "arn:aws:iam::123456789012:root"}), then every IAM entity in that account can assume the role — not just a specific service or user.
The confused deputy problem: When a service acts on behalf of a customer using cross-account access, an attacker can trick the service into accessing a different customer’s resources if the trust policy does not include an external ID condition.
How to Fix
Use IAM Access Analyzer to find cross-account access findings:
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/my-analyzer \
--filter '{"findingType":{"eq":["ExternalAccess"]},"resourceType":{"eq":["AWS::IAM::Role"]}}'
Always scope trust policies to specific principals, not account roots:
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/specific-service-role"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "unique-external-id-here"
}
}
}
Audit all existing cross-account trust relationships:
aws iam list-roles --query 'Roles[].{RoleName:RoleName,TrustPolicy:AssumeRolePolicyDocument}' | \
python3 -c "import json,sys; roles=json.load(sys.stdin);
[print(r['RoleName']) for r in roles
for s in r['TrustPolicy']['Statement']
if 'AWS' in s.get('Principal',{}) and ':root' in str(s['Principal']['AWS'])]"
Best practice: Never use account-level principals in trust policies. Always target a specific role or user ARN. Include sts:ExternalId conditions for third-party integrations. Use aws:PrincipalOrgID condition to restrict access to roles within your AWS Organization only.
4. Missing or Misconfigured Service Control Policies (SCPs)
Service Control Policies are permission guardrails applied at the AWS Organization level. They define the maximum permissions available to all principals in member accounts — including the account root user. Running an AWS Organization without SCPs is like having IAM policies without deny rules: everything is implicitly allowed up to the identity policy limit.
Why it matters: Without SCPs, any IAM administrator in any member account can grant any permission up to the full AWS service catalog. SCPs are the only mechanism to enforce invariants like “no one in this account can disable CloudTrail” or “no one can create IAM users with long-term credentials.”
Common SCP mistakes:
- Not attaching any SCPs beyond the default
FullAWSAccesspolicy - Attaching SCPs to the organization root instead of specific OUs, accidentally restricting management account actions
- Not protecting SCPs themselves (failing to deny
organizations:DetachPolicyororganizations:DeletePolicyin member accounts)
How to Fix
Verify what SCPs are currently attached:
aws organizations list-policies --filter SERVICE_CONTROL_POLICY
aws organizations list-targets-for-policy --policy-id p-xxxxxxxxxx
Implement baseline deny SCPs that every organization should have:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyDisablingCloudTrail",
"Effect": "Deny",
"Action": [
"cloudtrail:StopLogging",
"cloudtrail:DeleteTrail",
"cloudtrail:UpdateTrail"
],
"Resource": "*"
},
{
"Sid": "DenyLeavingOrganization",
"Effect": "Deny",
"Action": "organizations:LeaveOrganization",
"Resource": "*"
},
{
"Sid": "DenyRootUserActions",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:PrincipalArn": "arn:aws:iam::*:root"
}
}
}
]
}
Best practice: Organize accounts into OUs (workload, sandbox, security, infrastructure) and attach SCPs at the OU level. Deny dangerous actions by default — creating IAM users, disabling security services, modifying VPC flow logs, or accessing regions you do not use. Test SCPs in a sandbox OU before applying to production.
5. Permission Boundaries Not Enforced on Delegated Administration
Permission boundaries set the maximum permissions an IAM entity can have, regardless of its identity-based policies. They are critical when you delegate IAM administration to developers or application teams — without boundaries, a developer who can create roles can escalate privileges by creating a role with AdministratorAccess.
Why it matters: In organizations where developers manage their own IAM roles (common in DevOps environments), the absence of permission boundaries means any developer with iam:CreateRole and iam:AttachRolePolicy permissions can grant themselves or their services full administrative access. This is a privilege escalation path that bypasses all other controls.
The escalation chain: Developer has iam:CreateRole → creates a role with AdministratorAccess → assumes the role → now has full admin access. Permission boundaries break this chain by ensuring any role the developer creates is automatically limited.
How to Fix
Create a permission boundary policy that caps what delegated roles can do:
aws iam create-policy --policy-name DeveloperBoundary --policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"dynamodb:*",
"lambda:*",
"logs:*",
"sqs:*",
"sns:*"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"iam:CreateUser",
"iam:CreateAccessKey",
"organizations:*",
"account:*"
],
"Resource": "*"
}
]
}'
Require the boundary on all roles created by developers using a condition in their IAM policy:
{
"Effect": "Allow",
"Action": ["iam:CreateRole", "iam:AttachRolePolicy", "iam:PutRolePolicy"],
"Resource": "arn:aws:iam::*:role/app-*",
"Condition": {
"StringEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::123456789012:policy/DeveloperBoundary"
}
}
}
Best practice: Enforce permission boundaries via SCP for any account where IAM delegation exists. Name-prefix all delegated roles (e.g., app-*) and use IAM policy conditions to ensure boundaries are attached. Audit regularly for roles that bypass boundaries.
6. IAM Identity Center Misconfigurations
AWS IAM Identity Center (formerly AWS SSO) is now the recommended way to manage human access to AWS. However, misconfigurations here have an outsized blast radius because Identity Center manages access across your entire AWS Organization.
Why it matters: A single misconfigured permission set in IAM Identity Center can grant excessive access across dozens or hundreds of AWS accounts simultaneously. Unlike per-account IAM roles, Identity Center operates at the organization level — mistakes propagate everywhere.
Common misconfigurations:
- Overly broad permission set assignments: Assigning
AdministratorAccesspermission sets to groups that include non-admin users - Stale user assignments: Users who have changed teams or left the organization still retain access through group memberships that are not cleaned up in the identity provider
- Missing session duration limits: Default 1-hour sessions extended to 12 hours without justification, increasing the window of exposure if a session token is stolen
- No attribute-based access control (ABAC): Relying purely on group assignments instead of using session tags and ABAC policies to dynamically scope permissions based on user attributes
How to Fix
Audit current permission set assignments:
aws sso-admin list-instances
aws sso-admin list-permission-sets --instance-arn <instance-arn>
aws sso-admin list-account-assignments \
--instance-arn <instance-arn> \
--account-id <account-id> \
--permission-set-arn <permission-set-arn>
Review who has admin-level permission sets:
aws sso-admin list-permission-sets --instance-arn <instance-arn> --query 'PermissionSets' | \
xargs -I {} aws sso-admin describe-permission-set --instance-arn <instance-arn> --permission-set-arn {}
Set appropriate session durations (1 hour for privileged access):
aws sso-admin update-permission-set \
--instance-arn <instance-arn> \
--permission-set-arn <permission-set-arn> \
--session-duration PT1H
Best practice: Follow least-privilege for permission sets just as you would for IAM policies. Use inline policies within permission sets to scope access rather than attaching AWS managed policies like AdministratorAccess. Implement ABAC using Identity Center session tags to dynamically restrict access based on team, project, or environment attributes. Sync user lifecycle events from your IdP (Okta, Entra ID, Google Workspace) to automatically deprovision access when users change roles.
7. Ignoring IAM Access Analyzer Findings
IAM Access Analyzer is a free, built-in AWS service that continuously analyzes resource policies and IAM policies to identify unintended access. Despite being available since 2019 (with major expansions in 2023-2025 including unused access analysis and custom policy checks), many organizations either have not enabled it or ignore its findings.
Why it matters: Access Analyzer detects two categories of risk that manual reviews consistently miss:
- External access findings — resources (S3 buckets, KMS keys, SQS queues, Lambda functions, IAM roles) with policies that grant access to external entities outside your zone of trust
- Unused access findings — IAM roles, users, access keys, and permissions that exist but have not been used within a specified tracking period
Ignoring these findings means you are operating blind to both external exposure and accumulated privilege.
How to Fix
Enable Access Analyzer for external access (if not already active):
aws accessanalyzer create-analyzer \
--analyzer-name external-access \
--type ORGANIZATION
Enable Access Analyzer for unused access:
aws accessanalyzer create-analyzer \
--analyzer-name unused-access \
--type ORGANIZATION_UNUSED_ACCESS \
--configuration '{"unusedAccess":{"unusedAccessAge":90}}'
List all active findings:
aws accessanalyzer list-findings-v2 \
--analyzer-arn <analyzer-arn> \
--filter '{"status":{"eq":["ACTIVE"]}}'
Use custom policy checks in CI/CD to prevent new misconfigurations:
aws accessanalyzer check-access-not-granted \
--policy-document file://policy.json \
--access '[{"actions":["s3:*"],"resources":["*"]}]' \
--policy-type IDENTITY_POLICY
Best practice: Integrate Access Analyzer findings into your security operations workflow. Treat active findings as security incidents that require resolution within a defined SLA (e.g., critical findings within 48 hours, high within 1 week). Use the check-access-not-granted and check-no-new-access API calls in your CI/CD pipeline to prevent policies that grant public or cross-account access from ever being deployed.
8. Identity Federation Without Proper Scope Controls
Many organizations federate identity from external providers (Okta, Entra ID, Ping Identity) into AWS via SAML or OIDC. The federation itself is a best practice — it eliminates long-term IAM credentials. But misconfigured federation trust can be as dangerous as the credentials it replaces.
Why it matters: A compromised identity provider or a misconfigured OIDC trust can grant access to your entire AWS environment. If the SAML/OIDC trust is configured with broad role-mapping rules (e.g., any authenticated user from the IdP can assume an admin role), then a single compromised IdP account can escalate to full AWS administrative access.
Common misconfigurations:
- Overly broad audience/subject conditions in OIDC trust policies: Allowing any subject from a GitHub organization to assume a deployment role, instead of restricting to specific repositories and branches
- SAML role mapping without attribute filtering: Mapping IdP groups to AWS roles without restricting which group attributes are accepted
- Missing condition keys on federation trust policies: Not using
sts:SourceIdentity,aws:RequestedRegion, orsts:RoleSessionNameconditions to restrict and audit federated sessions
How to Fix
For GitHub Actions OIDC — scope to specific repositories and branches:
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"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"
}
}
}
For SAML federation — restrict role assumption to specific IdP groups:
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:saml-provider/MyIdP"
},
"Action": "sts:AssumeRoleWithSAML",
"Condition": {
"StringEquals": {
"SAML:aud": "https://signin.aws.amazon.com/saml",
"SAML:sub_type": "persistent"
},
"ForAnyValue:StringLike": {
"SAML:NameQualifier": "https://my-idp.example.com"
}
}
}
Best practice: Apply the principle of least privilege to federation trust policies. For OIDC (CI/CD pipelines), scope to specific repositories, branches, and environments. For SAML (workforce), map each IdP group to a dedicated AWS role with scoped permissions. Enable sts:SourceIdentity to permanently tag federated sessions for auditability. Monitor CloudTrail for AssumeRoleWithSAML and AssumeRoleWithWebIdentity events with unexpected subject claims.
A Practical IAM Audit Checklist
Use this checklist to assess your current IAM posture:
| Check | Tool | Frequency |
|---|---|---|
| Policies with wildcard actions or resources | IAM Access Analyzer custom checks | Every deployment (CI/CD) |
| Unused roles, keys, and permissions | IAM Access Analyzer (unused access) | Weekly automated review |
| Cross-account trust with account-level principals | IAM Access Analyzer (external access) | Continuous monitoring |
| SCPs enforcing baseline denies | AWS Organizations console | Quarterly review |
| Permission boundaries on delegated roles | IAM policy audit | Monthly |
| Identity Center permission set hygiene | SSO Admin API | Monthly |
| Federation trust policy scope | IAM role trust policy review | Quarterly |
| Root account credentials and MFA status | Credential report | Weekly |
Closing Thoughts
AWS IAM is not a set-and-forget service. It is a living system that accumulates risk over time as teams add permissions, create roles, configure trust relationships, and integrate external identities. The misconfigurations outlined in this article are not theoretical — they appear in breach reports, penetration test findings, and IAM Access Analyzer dashboards across organizations of every size.
The common thread across all of these issues is drift from least privilege. Permissions are granted for immediate needs and never revisited. Trust relationships are configured broadly and never scoped down. Access Analyzer findings accumulate without review.
The fix is not a one-time remediation sprint. It is an operational discipline: continuous monitoring with IAM Access Analyzer, preventive controls with SCPs and permission boundaries, and regular reviews of who and what has access to your environment.
How Can Cloudanix Help?
Cloudanix continuously monitors your AWS IAM configuration, detecting overly permissive policies, unused permissions, cross-account trust issues, and compliance gaps. Our platform surfaces actionable findings with remediation guidance — helping your team close IAM gaps before they become incidents. Start with a free trial to see your current IAM posture.
People Also Read
- A Complete List of AWS IAM Misconfigurations
- AWS IAM Permission Boundary and Why You Shouldn’t Ignore It
- Why IAM in the Cloud needs attention?
- Implementing IAM in the Google Cloud Platform (GCP)
- Elevate your Security with IAM Just-In-Time (JIT) Access
- Top 15 Cloud Misconfigurations in 2026 - How to Fix Them?
- How to setup Multi Factor Authentication (MFA) devices in AWS?
- Top 10 Identity and Access Management Solutions