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.
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.
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