AWS Builder Center
AWS IAM Security Best Practices: The Enterprise Zero Trust Guide

AWS IAM Security Best Practices: The Enterprise Zero Trust Guide

Master AWS IAM security with enterprise Zero Trust practices for ABAC, OIDC, IMDSv2, SCPs, RCPs, detection, CI/CD guardrails, vendors, and IAM automation.

AWS IAM gets dangerous at scale because permissions outlive the context that justified them. A role is added, a vendor is connected, a microservice ships, and six months later nobody remembers why a policy still has broad access. Enterprise Zero Trust is not one policy. It is a system that makes access narrow, temporary, contextual, observable, and removable.

Table of Contents

  1. Multi-account foundation and continuous reduction
  2. Eliminate permanent human credentials
  3. Protect workload credentials with roles, IMDSv2, and Roles Anywhere
  4. Make authentication context part of authorization
  5. Scale least privilege with ABAC
  6. Defend resources, not just identities
  7. Prevent service-to-service confused deputy attacks
  8. Lock down third-party vendors with ExternalId
  9. Federate CI/CD with OIDC
  10. Enforce permissions boundaries
  11. Model privilege escalation beyond iam:PassRole
  12. Build a network-aware data perimeter
  13. Shift IAM security left into CI/CD
  14. Detect and respond to IAM abuse
  15. Handle leaked credentials like an incident
  16. Harden and centralize root access
  17. Continuously Reduce Permissions

The One-Page IAM Cheat Sheet

Security problemEnterprise control
Human long-term credentialsFederation + temporary STS sessions
Workload credentialsIAM roles + IMDSv2
External/on-prem workloadsIAM Roles Anywhere
Role explosionABAC + governed tags
Sensitive data exposureResource policies + RCPs
Third-party SaaS accesssts:ExternalId
AWS service confused deputyaws:SourceArn, aws:SourceAccount, aws:SourceOrgID
CI/CD credentialsOIDC federation
Delegated IAM administrationEnforced permissions boundaries
Privilege escalationGraph/path analysis + dangerous-action controls
Network escapeVPC endpoint policies + aws:SourceVpce
Bad IaCCheckov/tfsec/CloudFormation Guard + Access Analyzer
Credential misuseCloudTrail + GuardDuty + EventBridge/CloudWatch
Posture driftConfig + Security Hub CSPM
Root riskCentralized root access + phishing-resistant MFA
Permission entropyAccess Analyzer + regular removal

1. Start With a Multi-Account Foundation—and Keep Permissions Shrinking

Accounts are stronger isolation boundaries than IAM policies inside one giant account. A mature AWS Organization normally separates management, security/audit, log archive, infrastructure, production, non-production, and sandbox workloads into purpose-built accounts and OUs.
SCPs define permission ceilings for principals in member accounts; they do not grant access, do not restrict the Organizations management account, and do not restrict service-linked roles. RCPs provide the resource-side complement, but only for supported services. Design the Organizations/OU structure before relying on either.
The same architecture needs a lifecycle. IAM Access Analyzer should validate policies before deployment and later help identify external, internal, and unused access. The operating loop is simple: Design → Test → Deploy → Observe → Reduce → Revalidate.
AWS Organizations hierarchy showing the Organization Root and Management Account above Security, Infrastructure, Workload, and Sandbox OUs. Each OU contains member accounts, with SCPs governing principals and RCPs governing protected resources such as S3, KMS, SQS, and Secrets Manager.
Enterprise AWS Organizations hierarchy showing how SCPs govern principals and RCPs govern protected resources across Security, Infrastructure, Workload, and Sandbox organizational units.

2. Eliminate Permanent Human Credentials

Human access should begin with your identity provider.
Not with:
1
2
3
CreateUser
CreateAccessKey
Send credentials
For workforce access, prefer federation into temporary sessions.
AWS explicitly recommends federation and temporary credentials for human access.
The desirable lifecycle is:
Corporate identity → authentication → authorization → temporary role session → expiration
That last word matters.
Expiration is a security control.
A permanent access key remains useful until somebody notices and revokes it.
A temporary session dies on schedule.
This also enables cleaner privilege tiers.
An engineer might normally receive read-only access, temporarily elevate into a production-operator role during an incident, and use tightly controlled emergency access only under exceptional circumstances.
Do not turn today's requirement into next year's permanent entitlement.

3. Protect Workload Credentials With Roles, IMDSv2, and Roles Anywhere

Roles remove the need to store AWS secrets in .env files, containers, build systems, repositories, or deployment scripts. They do not make role credentials impossible to steal.
The Capital One breach is the classic lesson: an application-layer weakness and SSRF path were used to reach the EC2 metadata service, obtain temporary role credentials, and use permissions available to that role. The lesson is not that IMDSv2 alone would have prevented the entire incident; it is that temporary credentials still need delivery-path hardening and least privilege.
Require IMDSv2 for EC2:
1
2
3
4
5
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type c6i.large \
--metadata-options \
"HttpEndpoint=enabled,HttpTokens=required,HttpPutResponseHopLimit=2"
For container hosts, the hop limit often needs to be 2; validate against your runtime. Enforce IMDSv2 through account defaults, IaC policy checks, Config/Control Tower controls where appropriate, and deployment standards.
A role can still be dangerous if it contains:
1
2
3
4
5
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
Temporary credentials limit persistence. Least privilege limits blast radius.
For workloads outside AWS, do not jump straight to long-term keys. Prefer native workload identity, OIDC/SAML federation, or IAM Roles Anywhere with X.509 certificates and temporary role credentials.

4. Make Authentication Context Part of Authorization

"MFA enabled" is not the same as "MFA affects privilege." For IAM-user flows, sensitive actions can be denied when MFA context is absent:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenySensitiveActionsWithoutMFA",
"Effect": "Deny",
"Action": [
"ec2:TerminateInstances",
"iam:CreateAccessKey",
"iam:DeleteRole"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
aws:MultiFactorAuthPresent and aws:MultiFactorAuthAge do not behave the same way for federated or IAM Identity Center sessions. If MFA happens at the IdP, carry trusted authentication context into AWS through attributes or session tags instead of blindly copying an IAM-user condition.
For Region restrictions, avoid hard-coding an incomplete NotAction list of global services. Prefer a maintained Control Tower/Organizations control or a centrally managed baseline using aws:RequestedRegion.

5. Scale Least Privilege With ABAC

RBAC becomes expensive when every team, service, and environment needs another role. ABAC lets one policy authorize resources dynamically from trusted attributes.
A principal tagged Team=Payments can manage only resources with the same tag:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ManageInstancesOwnedBySameTeam",
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:RebootInstances"
],
"Resource": "arn:aws:ec2:*:111122223333:instance/*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/Team": "${aws:PrincipalTag/Team}"
}
}
}
]
}
Now new Team=Payments resources inherit the same authorization without editing ARN lists.
Tag-on-create must also be enforced. For EC2, allow CreateTags only during RunInstances and require the requested team to match the principal:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RequireTeamTagWhenLaunching",
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestTag/Team": "${aws:PrincipalTag/Team}"
}
}
},
{
"Sid": "AllowTeamTagDuringLaunch",
"Effect": "Allow",
"Action": "ec2:CreateTags",
"Resource": "*",
"Condition": {
"StringEquals": {
"ec2:CreateAction": "RunInstances",
"aws:RequestTag/Team": "${aws:PrincipalTag/Team}"
}
}
}
]
}
Protect authorization tags after creation:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyDirectModificationOfTeamTag",
"Effect": "Deny",
"Action": [
"ec2:CreateTags",
"ec2:DeleteTags"
],
"Resource": "arn:aws:ec2:*:111122223333:instance/*",
"Condition": {
"ForAnyValue:StringEquals": {
"aws:TagKeys": ["Team"]
},
"Null": {
"ec2:CreateAction": "true"
}
}
}
]
}
Session tags can carry Team, Department, or Environment from the IdP into STS sessions. If attributes need to survive role chaining, mark selected tags as transitive and authorize sts:TagSession.
Diagram showing AWS attribute-based access control (ABAC): a corporate IdP assigns `Team=Payments` to a user, the attribute becomes an STS `PrincipalTag`, and access is allowed only to resources tagged `Team=Payments`, while a `Team=Analytics` resource is denied.
AWS ABAC uses federated identity attributes and STS principal tags to automatically grant access only to resources with matching authorization tags.

6. Defend Resources, Not Just Identities

Identity policies answer what a principal may request. Resource policies answer who a resource will accept.
Within one account, applicable identity- and resource-based allows are generally evaluated as a union, subject to explicit denies and other controls. Cross-account access is different: the trusted account and the resource-owning account generally both need to authorize the interaction.
KMS is a notable special case because key policies are a mandatory part of authorization.
A sensitive S3 bucket can enforce an organization boundary directly:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyPrincipalsOutsideOrganization",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::company-sensitive-data",
"arn:aws:s3:::company-sensitive-data/*"
],
"Condition": {
"StringNotEqualsIfExists": {
"aws:PrincipalOrgID": "o-a1b2c3d4e5"
},
"BoolIfExists": {
"aws:PrincipalIsAWSService": "false"
}
}
}
]
}
Resource-side denies give you a second boundary even when an identity is over-permissioned.

7. Prevent Service-to-Service Confused Deputy Attacks

Trusting an AWS service principal is not always enough. Where supported, constrain the source resource or account that caused the service to call you:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowServiceOnlyForMyResources",
"Effect": "Allow",
"Principal": {
"Service": "example-service.amazonaws.com"
},
"Action": "service:RequiredAction",
"Resource": "arn:aws:service:us-east-1:111122223333:resource/example",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "111122223333"
},
"ArnLike": {
"aws:SourceArn": "arn:aws:source-service:us-east-1:111122223333:*"
}
}
}
]
}
At larger scale, aws:SourceOrgID and aws:SourceOrgPaths can provide organization context. Support is service-specific, so verify the target service documentation.

8. Lock Down Third-Party Vendors With ExternalId

A multi-tenant SaaS vendor that assumes roles in customer accounts should provide a unique sts:ExternalId. A vendor trust policy can require both the vendor principal and the tenant-specific value:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowApprovedVendorTenant",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::444455556666:role/CustomerAccessBroker"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "7f8d2677-7d7c-45ab-a97d-cf95f3028ad1"
}
}
}
]
}
ExternalId is tenancy context, not a password. Also restrict the vendor's actions, resources, session duration, logging path, and offboarding process.

9. Federate CI/CD With OIDC Instead of Access Keys

GitHub Actions and other CI/CD systems should obtain short-lived AWS sessions instead of storing long-term keys. For GitHub, the trust policy must tightly restrict token claims, especially sub:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::111122223333:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:<ORG>/<REPO>:ref:refs/heads/main"
}
}
}
]
}
Validate the actual sub your repository emits; GitHub Environments and newer subject formats can differ.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: deploy

on:
push:
branches: [main]

permissions:
contents: read
id-token: write

jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- uses: aws-actions/configure-aws-credentials@v6.3.0
with:
role-to-assume: arn:aws:iam::111122223333:role/github-production-deploy
aws-region: us-east-1

- run: aws sts get-caller-identity
OIDC removes the stored AWS secret; it does not make an over-privileged deployment role safe.

10. Enforce Permissions Boundaries, Don't Just Attach Them

A permissions boundary is useful only if delegated administrators cannot create roles without it or remove it later.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowRoleCreationWithApprovedBoundary",
"Effect": "Allow",
"Action": "iam:CreateRole",
"Resource": "arn:aws:iam::111122223333:role/application/*",
"Condition": {
"ArnEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::111122223333:policy/ApplicationRoleBoundary"
}
}
},
{
"Sid": "DenyRemovingBoundary",
"Effect": "Deny",
"Action": "iam:DeleteRolePermissionsBoundary",
"Resource": "arn:aws:iam::111122223333:role/application/*"
}
]
}
Also restrict who can modify the boundary policy itself and who may call PutRolePermissionsBoundary.

11. Model Privilege Escalation Beyond iam:PassRole

Privilege escalation is usually about combinations, not one permission. High-risk paths include:
  • iam:CreatePolicyVersion
  • iam:AttachRolePolicy
  • iam:PutRolePolicy
  • iam:UpdateAssumeRolePolicy
  • lambda:UpdateFunctionCode
  • ssm:SendCommand
  • iam:PassRole combined with permissions to create or update a service that uses the passed role
Graph-oriented tools such as PMapper can reveal paths between principals and privileged actions; Cloudsplaining can flag least-privilege risks such as privilege escalation, data exfiltration, credential exposure, and resource exposure.
The better question is not only, "What can this principal do now?" It is "What could this principal become?"

12. Build a Network-Aware Data Perimeter

Identity is only one signal. Resource access can also be constrained using aws:SourceVpc, aws:SourceVpce, aws:VpceAccount, aws:VpceOrgID, aws:ViaAWSService, and related conditions.
A sensitive S3 bucket might deny direct access outside an approved VPC endpoint:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnexpectedNetworkPath",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::company-sensitive-data",
"arn:aws:s3:::company-sensitive-data/*"
],
"Condition": {
"StringNotEqualsIfExists": {
"aws:SourceVpce": "vpce-0123456789abcdef0"
},
"BoolIfExists": {
"aws:ViaAWSService": "false",
"aws:PrincipalIsAWSService": "false"
}
}
}
]
}
AWS services can make downstream calls through Forward Access Sessions, where the original VPC context may not propagate. Account for aws:ViaAWSService or aws:CalledVia when legitimate service-to-service workflows require it. VPC endpoint policies can add another gate by restricting which principals and resources may be reached through the endpoint.

13. Shift IAM Security Left Into CI/CD

A pull request should fail before an unsafe IAM policy reaches AWS. Use IaC scanners such as Checkov or tfsec for infrastructure patterns, then use IAM Access Analyzer for policy-specific checks.
Validation:
1
2
3
aws accessanalyzer validate-policy \
--policy-document file://policy.json \
--policy-type IDENTITY_POLICY
Detect permission expansion:
1
2
3
4
aws accessanalyzer check-no-new-access \
--existing-policy-document file://policy-before.json \
--new-policy-document file://policy-after.json \
--policy-type IDENTITY_POLICY
Assert forbidden permissions are absent:
1
2
3
4
aws accessanalyzer check-access-not-granted \
--policy-document file://policy.json \
--policy-type IDENTITY_POLICY \
--access 'actions="iam:CreateUser","iam:AttachRolePolicy","organizations:LeaveOrganization"'
A compact GitHub Actions gate can combine Checkov, OIDC, and Access Analyzer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
name: iam-security

on:
pull_request:

permissions:
contents: read
id-token: write

jobs:
iam-security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Install Checkov
run: pip install checkov

- name: Scan Terraform
run: checkov -d . --framework terraform

- uses: aws-actions/configure-aws-credentials@v6.3.0
with:
role-to-assume: arn:aws:iam::111122223333:role/github-iam-validator
aws-region: us-east-1

- name: Validate policy
shell: bash
run: |
RESULT=$(aws accessanalyzer validate-policy \
--policy-document file://iam/policy.json \
--policy-type IDENTITY_POLICY)
echo "$RESULT"
echo "$RESULT" | jq -e '
[.findings[] |
select(.findingType == "ERROR" or
.findingType == "SECURITY_WARNING")] |
length == 0'

- name: Reject forbidden access
shell: bash
run: |
RESULT=$(aws accessanalyzer check-access-not-granted \
--policy-document file://iam/policy.json \
--policy-type IDENTITY_POLICY \
--access 'actions="iam:CreateAccessKey","iam:AttachRolePolicy","iam:CreatePolicyVersion"')
echo "$RESULT"
echo "$RESULT" | jq -e '.result == "PASS"'
Not every "Resource": "*" is automatically wrong; some AWS actions do not support resource-level authorization. Build exceptions from the Service Authorization Reference instead of using string matching as security policy.

14. Detect IAM Abuse After Prevention Fails

This is the layer most IAM articles forget.
Prevention asks:
Should this happen?
Detection asks:
What if it did?
Your IAM architecture needs both.

CloudTrail is your identity flight recorder

CloudTrail records IAM and STS API activity, including assumed-role context.
For temporary credentials, CloudTrail's userIdentity data can identify the role session and session issuer. STS sourceIdentity, when configured and propagated, can make role-chaining investigations substantially easier because it preserves information about the original identity.
That means your investigation should be able to answer:
Who assumed the role?
From where?
When?
Using which source identity?
Which role session?
What APIs were called afterward?

Alert on root activity

Root should be so rare that use itself is a security event.
AWS Security Hub's CIS-aligned control uses a CloudTrail metric-filter pattern for root activity:
1
2
3
{$.userIdentity.type="Root" &&
$.userIdentity.invokedBy NOT EXISTS &&
$.eventType !="AwsServiceEvent"}
Connect that metric to a CloudWatch alarm, SNS, incident-management platform, or SIEM.

Alert on IAM mutation

CloudTrail documents monitoring policy changes such as:
1
2
3
4
5
6
7
CreatePolicy
CreatePolicyVersion
PutRolePolicy
AttachRolePolicy
AttachUserPolicy
DeletePolicy
DetachRolePolicy
through CloudWatch Logs metric filters and alarms.
I would extend the enterprise detection set to include high-signal events such as:
1
2
3
4
5
6
7
CreateAccessKey
UpdateAssumeRolePolicy
PutRolePermissionsBoundary
DeleteRolePermissionsBoundary
CreateLoginProfile
DeactivateMFADevice
DeleteVirtualMFADevice
Whether each generates a page, ticket, or informational event depends on your environment.

EventBridge makes IAM changes routable

IAM API activity delivered through CloudTrail can be matched by EventBridge.
For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"source": [
"aws.iam"
],
"detail-type": [
"AWS API Call via CloudTrail"
],
"detail": {
"eventSource": [
"iam.amazonaws.com"
],
"eventName": [
"CreateAccessKey",
"CreatePolicyVersion",
"AttachRolePolicy",
"UpdateAssumeRolePolicy",
"DeleteRolePermissionsBoundary"
]
}
}
AWS documents the EventBridge pattern for routing specific IAM API calls from CloudTrail.

Do not alert on every AssumeRole

In a healthy AWS estate, AssumeRole may happen thousands or millions of times.
An alarm on every event becomes wallpaper.
Instead, detect unusual role usage.
Examples include:
a role used from a new ASN or geography, unusual API sequences, unusual role sessions, first-time access patterns, high-risk roles being assumed outside expected workflows, or role use inconsistent with the normal principal.
Amazon GuardDuty profiles API activity and can create anomalous-behavior findings based on user identity, API, location, user agent, and related behavior.
CloudTrail tells you what happened.
GuardDuty helps tell you what looks strange.

Config and Security Hub cover posture

Detection is also about configuration drift.
Security Hub CSPM currently includes IAM controls covering issues such as full administrative wildcard policies, root access keys, MFA, unused credentials, role tagging, and Access Analyzer.
AWS Config can continuously evaluate configuration rules, including root MFA controls.
These tools should feed a central security account or SIEM.
Do not leave security findings trapped inside twenty-seven individual AWS consoles.
Diagram showing an AWS security detection loop where CloudTrail captures IAM events such as root activity, CreateAccessKey, UpdateAssumeRolePolicy, and anomalous AssumeRole, which are analyzed by GuardDuty, Security Hub, and Config, routed through EventBridge and CloudWatch to a central security account or SIEM, and then handled through incident response.
AWS detection and response architecture combining CloudTrail, GuardDuty, Security Hub, Config, EventBridge, CloudWatch, and a central SIEM to identify, alert on, investigate, and respond to suspicious IAM activity.

15. Handle Leaked Credentials Like an Incident

Long-term credentials should be rare.
Rare does not mean impossible.
If you still maintain access keys, assume one will eventually appear somewhere it should not.
Use secret scanning in source control.
Monitor public exposure notifications.
And document the response before the incident.
When a key is suspected of compromise:
Deactivate it.
Identify the owning principal.
Review CloudTrail activity associated with it.
Determine what the principal could access.
Look for persistence mechanisms or privilege changes.
Replace the credential only if it is still legitimately required.
Then remove unnecessary access.
GuardDuty publishes guidance for investigating potentially compromised IAM credentials.
AWS also maintains AWSCompromisedKeyQuarantineV3, a managed quarantine policy that AWS may apply when it determines an IAM user's credentials were exposed or compromised. AWS explicitly says not to simply remove that policy; follow the associated support instructions.
Treat a leaked key as:
possible account compromise
not:
password rotation ticket.

16. Harden and Centralize Root Access

The root user should not be your super-admin account.
It should be a break-glass identity.
For accounts where root credentials remain, protect them with phishing-resistant MFA. AWS also provides Security Hub and Config controls specifically for root MFA and hardware MFA posture.
Do not create root access keys.
AWS explicitly recommends against root access keys because root has unrestricted account authority.
And alert whenever root is used.
But modern multi-account AWS environments can go further.

Centralize root access for member accounts

AWS Organizations now supports centralized root access management for member accounts.
Organizations can remove member-account root passwords, access keys, signing certificates, and MFA credentials and prevent member accounts from recovering normal root credentials. New organization member accounts can also be created without root credentials by default.
For the exceptional operations that really require root authority, authorized administrators can use centrally controlled privileged root sessions for supported tasks rather than maintaining everyday root credentials in every account.
That changes the model from:
protect 500 permanent root identities
to:
remove the permanent credentials and centrally broker exceptional root operations.
That is a much more scalable enterprise control.

17. Continuously Reduce Permissions

IAM is not finished when Terraform applies.
A perfectly justified permission today can become unnecessary in six months.
A role gets replaced.
A microservice changes.
A vendor contract ends.
A team migrates platforms.
The permission stays.
That is permission entropy.
IAM Access Analyzer should therefore exist at both ends of the lifecycle.
Before deployment:
validate policies.
check forbidden access.
detect permission expansion.
After deployment:
analyze external access.
review internal access.
identify unused roles and permissions.
generate tighter policies from observed activity where appropriate.
AWS explicitly recommends Access Analyzer for validation, least-privilege policy generation, public/cross-account access analysis, and unused-access review.
The mature IAM lifecycle is:
Design → Test → Deploy → Observe → Detect → Reduce → Revalidate
Not:
Create → Forget

What About Region Restrictions?

Region controls are useful.
But do not copy a five-line blog policy with an incomplete NotAction list and assume the job is finished.
aws:RequestedRegion policies need exceptions for global services, and the correct exception set changes as AWS services evolve.
AWS's IAM documentation explicitly warns that global services require special handling. AWS Control Tower also provides maintained Region-deny controls for landing zones and individual OUs.
For enterprise environments, prefer a maintained organizational control or generate the policy from a controlled baseline rather than freezing an old list of global services inside a forgotten SCP.

The Enterprise Zero Trust Model for AWS

You can reduce this entire article to nine questions.
Identity: Who are you?
Authentication: How did you prove it?
Attributes: What context does your session carry?
Authorization: What may you request?
Resource: Will the resource accept this interaction?
Organization: Does this violate a global guardrail?
Network: Did the request arrive through an expected path?
Detection: Does the behavior look normal?
Lifecycle: Should this permission still exist?
Most IAM failures happen because organizations answer the first four and forget the rest.

Final AWS IAM Security Checklist

Before describing your AWS environment as Zero Trust, verify the entire system rather than one policy.
For humans, use federation, temporary sessions, phishing-resistant authentication, and short-lived privileged access. For AWS workloads, use roles, enforce IMDSv2, minimize the role's authority, and use IAM Roles Anywhere or federation instead of static keys for external workloads.
At scale, combine RBAC with ABAC, source authorization attributes from trusted systems, require tags at creation, and protect the tags that drive authorization. Use resource policies alongside identity policies, understand the difference between same-account and cross-account evaluation, and remember that KMS has special policy semantics.
For third parties, require narrowly scoped role trust and unique sts:ExternalId values. For AWS services, constrain service principals with aws:SourceArn, aws:SourceAccount, or organization-level source conditions where supported. For GitHub Actions and other CI/CD systems, use OIDC instead of stored AWS keys and tightly scope token claims.
Use permissions boundaries as enforceable ceilings for delegated IAM administration, not optional attachments. Model privilege-escalation paths beyond iam:PassRole, including IAM policy mutation, trust-policy modification, code deployment into privileged workloads, and remote command capabilities.
At the organization level, build the correct multi-account and OU structure before relying on SCPs or RCPs. Remember that SCPs do not constrain the management account or service-linked roles, and RCP service coverage is not universal.
At the network layer, use VPC endpoint policies and network-aware condition keys, but account for AWS service calls and Forward Access Sessions.
Before deployment, scan IaC and run Access Analyzer validation, no-new-access checks, and forbidden-access assertions. After deployment, collect CloudTrail centrally, use GuardDuty for anomalous activity, use Security Hub and Config for posture, and alert on high-risk IAM mutations and root activity.
And finally, practice credential response before credentials leak.
Because "verify continuously" is meaningless if nobody is watching.

The Principle That Matters

AWS IAM becomes difficult when organizations try to solve every security problem with another policy.
That is not the answer.
The answer is architecture.
Temporary credentials reduce persistence.
Least privilege reduces blast radius.
ABAC reduces policy sprawl.
Resource policies defend data.
SCPs and RCPs create ceilings.
OIDC removes CI/CD secrets.
External IDs isolate vendor tenants.
IMDSv2 hardens workload credentials.
VPC conditions constrain paths.
CI/CD checks prevent mistakes.
CloudTrail records reality.
GuardDuty looks for abnormal behavior.
Access Analyzer removes yesterday's privileges.
None of these controls is Zero Trust by itself.
Together, they create something much more useful:
Trust that is narrow.
Trust that has context.
Trust that expires.
Trust that is observable.
And most importantly:
Trust that can be revoked before a small mistake becomes a large breach.

Diagram showing CloudTrail capturing events such as root activity, CreateAccessKey, UpdateAssumeRolePolicy, and anomalous AssumeRole, followed by analysis in GuardDuty, Security Hub, and Config, routing through EventBridge to a central SIEM or security account, and incident response.
AWS detection architecture combining CloudTrail, GuardDuty, Security Hub, Config, EventBridge, centralized SIEM monitoring, and incident response to identify and investigate suspicious IAM activity.

Incident-learning diagram showing a WAF compromise leading to SSRF, access to EC2 metadata credentials, use of an over-privileged IAM role, and S3 access. The graphic notes that IMDSv2 alone should not be viewed as a complete prevention for the breach.
Simplified Capital One incident-learning flow illustrating how an application-layer compromise can escalate into cloud data access when metadata credentials and overly broad IAM permissions are involved.

Diagram showing a GitHub Actions workflow issuing an OIDC JWT, validating a restricted sub claim, exchanging the token through AWS STS for temporary credentials, running IAM validation checks, and deploying to AWS without permanent access keys.
GitHub Actions can securely access AWS through OIDC and short-lived STS credentials, with tightly scoped token claims and IAM validation before deployment.

Diagram showing a corporate identity provider assigning Team=Payments, AWS STS carrying the attribute as a PrincipalTag, and tag-based authorization allowing access to Payments-tagged resources while denying access to an Analytics-tagged resource.
AWS ABAC scales authorization by matching trusted principal attributes with resource tags, allowing access automatically when tags match and denying access when they do not.

Side-by-side diagram comparing AWS IAM policy evaluation. Same-account access uses the union of applicable identity-based and resource-based allows, subject to explicit denies and other controls. Cross-account access requires authorization from both the trusted account and the resource-owning account.
AWS IAM evaluates same-account and cross-account requests differently: same-account permissions can come from applicable identity or resource policies, while cross-account access generally requires both sides to authorize the request.

AWS IAM security best practices: AWS IAM Security Best Practices 
AWS IAM policy evaluation: AWS Policy Evaluation Logic 
AWS data perimeters: AWS Data Perimeter Guidance 
IAM Access Analyzer custom policy checks: Access Analyzer Policy Checks 
AWS multi-account strategy: AWS Control Tower Multi-Account Strategy 
IAM Roles Anywhere: IAM Roles Anywhere Guide 
Centralized root management: Centralize Root Access 
GitHub OIDC for AWS: GitHub OIDC AWS Guide 
Employee authenticates through a corporate identity provider and IAM Identity Center to receive a temporary AWS role that expires, contrasted with long-lived IAM user access keys.
AWS human access should rely on federated identity and temporary roles rather than permanent IAM user credentials.
Diagram showing how permissions such as CreatePolicyVersion and PassRole can combine across policies, roles, and AWS compute services to create a path from developer access to administrator privileges.
IAM privilege escalation often results from combinations of permissions that create a path to more privileged roles.
AWS authorization flow showing an identity policy determining whether a principal can make a request, a resource policy determining whether the resource accepts it, and explicit deny overriding access.
AWS authorization can depend on both identity and resource policies, with an explicit deny taking precedence.
Comparison showing an attacker abusing a trusted AWS service to access another resource, and protection using SourceAccount, SourceArn, and SourceOrgID conditions to restrict the service request.
Confused deputy protections constrain trusted AWS services so they can access only the intended account, resource, or organization.
AWS data perimeter diagram showing a user reaching an S3 bucket through a VPC and VPC endpoint while direct internet access is blocked, combining identity, organization, network path, and resource policy controls.
A data perimeter combines identity, organizational boundaries, trusted network paths, and resource policies to control access to sensitive AWS data.

Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article