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
  1. Eliminate permanent human credentials
  1. Protect workload credentials with roles, IMDSv2, and Roles Anywhere
  1. Make authentication context part of authorization
  1. Scale least privilege with ABAC
  1. Defend resources, not just identities
  1. Prevent service-to-service confused deputy attacks
  1. Lock down third-party vendors with ExternalId
  1. Federate CI/CD with OIDC
  1. Enforce permissions boundaries
  1. Model privilege escalation beyond  iam:PassRole 
  1. Build a network-aware data perimeter
  1. Shift IAM security left into CI/CD
  1. Detect and respond to IAM abuse
  1. Handle leaked credentials like an incident
  1. Harden and centralize root access
  1. Continuously Reduce Permissions



The One-Page IAM Cheat Sheet

Security problem
Enterprise control
Human long-term credentials
Federation + temporary STS sessions
Workload credentials
IAM roles + IMDSv2
External/on-prem workloads
IAM Roles Anywhere
Role explosion
ABAC + governed tags
Sensitive data exposure
Resource policies + RCPs
Third-party SaaS access
sts:ExternalId
AWS service confused deputy
aws:SourceArn, aws:SourceAccount, aws:SourceOrgID
CI/CD credentials
OIDC federation
Delegated IAM administration
Enforced permissions boundaries
Privilege escalation
Graph/path analysis + dangerous-action controls
Network escape
VPC endpoint policies + aws:SourceVpce
Bad IaC
Checkov/tfsec/CloudFormation Guard + Access Analyzer
Credential misuse
CloudTrail + GuardDuty + EventBridge/CloudWatch
Posture drift
Config + Security Hub CSPM
Root risk
Centralized root access + phishing-resistant MFA
Permission entropy
Access 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.




2. Eliminate Permanent Human Credentials

Human access should begin with your identity provider.
Not with:
Plain text
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:
Plain text
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:
Plain text
{
"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:
Plain text
{
"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:
Plain text
{
"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:
Plain text
{
"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:
Plain text
{
"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.




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:
Plain text
{
"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:
Plain text
{
"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:
Plain text
{
"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 :
Plain text
{
"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.
Plain text
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.
Plain text
{
"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:
Plain text
{
"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:
Plain text
aws accessanalyzer validate-policy \
--policy-document file://policy.json \
--policy-type IDENTITY_POLICY
Detect permission expansion:
Plain text
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:
Plain text
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:
Plain text
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:
Plain text
{$.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:
Plain text
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:
Plain text
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:
Plain text
{
"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.




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.




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.




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.




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.




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

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.

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.

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.

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.




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