14.1 IAM Policy Evaluation Logic, NotAction & Explicit Denies
Key Takeaways
- Default Deny is the baseline for all AWS API calls; an authorization decision requires at least one explicit Allow and zero explicit Denies across all applicable policy categories.
- An Explicit Deny in any applicable policy (SCP, identity-based policy, resource-based policy, permissions boundary, or session policy) immediately halts evaluation and overrides all explicit Allows.
- When an identity-based policy, a permissions boundary, and an STS session policy are all present, the effective permission is strictly the intersection (logical AND) of all three policy scopes.
- Same-account authorization requires an explicit Allow from either an identity-based policy OR a resource-based policy, whereas cross-account access requires explicit Allows in BOTH the identity policy AND the target resource policy (or role trust policy).
- The NotAction element paired with Effect: Deny creates a comprehensive deny across all AWS actions except those explicitly listed, whereas NotAction paired with Effect: Allow grants access to all actions except those listed.
AWS IAM Policy Evaluation Logic & Decision Flow
Every incoming AWS API call—whether originated by a developer via the AWS CLI, an automated CI/CD pipeline role, an Amazon EC2 instance profile, or a serverless AWS Lambda function—is evaluated by the AWS Identity and Access Management (IAM) authorization engine. Understanding the precise mathematical and logical precedence of this evaluation engine is crucial for passing the AWS Certified DevOps Engineer - Professional (DOP-C02) exam.
The Fundamental Axioms of IAM Evaluation
The IAM evaluation engine operates on three deterministic axioms:
- Default Deny (Implicit Deny): By default, all requests are denied. If no policy explicitly grants permission for an action on a resource, the final authorization decision is
Deny. - Explicit Deny Precedence: An explicit
Denyin any policy that applies to the request immediately supersedes any and all explicitAllowstatements. If a single applicable policy evaluates toDeny, the final authorization decision is unconditionallyDeny. - Explicit Allow Requirement: In the absence of an explicit
Deny, there must be at least one explicitAllowin an applicable policy for the request to succeed.
Request Received
│
▼
[ Is there an Explicit Deny in ANY applicable policy? ] ──YES──> DENY
│
NO
▼
[ Do Organizations SCPs Allow the Action? ] ─────────────NO───> DENY
│
YES
▼
[ Is there a Permissions Boundary? ] ────────────────────YES──> [ Does Boundary Allow Action? ] ──NO──> DENY
│ │ YES
NO ▼
│ ◄─────────────────────────────────────────────────────────────────────┘
▼
[ Is there an STS Session Policy? ] ─────────────────────YES──> [ Does Session Policy Allow Action? ] ──NO──> DENY
│ │ YES
NO ▼
│ ◄─────────────────────────────────────────────────────────────────────┘
▼
[ Is this a Cross-Account Request? ]
│
├─── YES ──> [ Do BOTH Identity Policy AND Resource Policy Allow Action? ] ──NO──> DENY
│ │ YES
│ ▼
│ ALLOW
│
└─── NO ───> [ Does EITHER Identity Policy OR Resource Policy Allow Action? ] ──YES──> ALLOW
│ NO
▼
DENY (Default Implicit Deny)
Multi-Policy Types and Authorization Intersections
In an enterprise environment governed by AWS Organizations and landing zones (such as AWS Control Tower), multiple policy types simultaneously govern a single request. The effective permissions represent the logical intersection or union depending on the policy types involved.
Applicable Policy Types
| Policy Type | Attachment Point | Scope & Function | Evaluation Behavior |
|---|---|---|---|
| Service Control Policy (SCP) | AWS Organizations Root, OU, or Account | Maximum entitlement guardrail across an entire account | Must allow the action; does not grant permissions to identities directly |
| Resource-Based Policy | AWS Resources (S3 buckets, KMS keys, SQS queues, Secrets) | Specifies which principals can access the specific resource | Can grant access directly across accounts without role assumption |
| IAM Permissions Boundary | IAM Users or IAM Roles | Maximum entitlement ceiling for an identity | Restricts identity-based policies; effective permission is Identity ∩ Boundary |
| STS Session Policy | Temporary STS credentials (AssumeRole, GetFederationToken) | Ephemeral permission filter passed dynamically during role assumption | Restricts assumed session; effective permission is Role ∩ SessionPolicy |
| Identity-Based Policy | IAM Users, IAM Groups, or IAM Roles | Grants permissions directly to the authenticated identity | Evaluated against resource policies, boundaries, and session policies |
The Intersection Rule for Constrained Identities
When an IAM entity operates with both a Permissions Boundary and an STS Session Policy, the effective permissions are determined by the strict intersection (logical AND) of all three policy scopes:
If an identity policy grants s3:* and ec2:*, but the permissions boundary only allows s3:* and dynamodb:*, the intermediate allowable scope is s3:*. If the role is assumed via STS with a session policy allowing s3:GetObject and sqs:*, the final authorized permission is strictly s3:GetObject.
Same-Account vs. Cross-Account Evaluation Mechanics
The rules governing resource-based policies differ fundamentally based on whether the principal and the resource reside in the same AWS account or different AWS accounts.
1. Same-Account Access
Within the same AWS account, an explicit Allow in either the identity-based policy or the resource-based policy is sufficient to grant access, assuming no explicit Deny exists.
- If an IAM role has no identity-based policies attached, but an Amazon S3 bucket policy in the same account explicitly allows that IAM role ARN to perform
s3:GetObject, the role is authorized. - Critical Exception (AWS KMS): AWS Key Management Service (KMS) key policies behave differently. If a KMS key policy does not delegate administration to the account root principal (
"Principal": {"AWS": "arn:aws:iam::111122223333:root"}), identity-based policies cannot grant access to that key even within the same account. The key policy itself must contain the explicit allow.
2. Cross-Account Access
When a principal in Account A requests access to a resource in Account B:
- An explicit
Allowis mandatory in both the calling principal's identity-based policy (Account A) and the target resource-based policy (Account B). - If the target resource does not support resource-based policies (for example, an Amazon EC2 instance, an Amazon RDS DB instance, or an Amazon EBS volume), the principal in Account A cannot access the resource directly; it must assume an IAM role in Account B via
sts:AssumeRole. The set of services that support resource-based policies grows over time — Amazon S3, AWS KMS, Amazon SQS, Amazon SNS, AWS Secrets Manager, AWS Lambda, Amazon EventBridge, Amazon ECR, and Amazon DynamoDB all support them today.
The NotAction Element: The Critical Exam Trap
One of the most dangerous and heavily tested concepts on the DOP-C02 exam is the interaction between NotAction and the Effect element (Allow vs. Deny).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DangerousPolicyExamTrap",
"Effect": "Deny",
"NotAction": "s3:*",
"Resource": "*"
}
]
}
What Does Effect: Deny with NotAction Actually Do?
Many engineers mistakenly believe the policy above means "Deny access to all S3 actions that are not specified." That is completely incorrect.
NotAction: s3:*matches every single AWS action in existence except S3 actions (ec2:*,rds:*,iam:*,lambda:*,dynamodb:*, etc.).Effect: Denyapplies an explicit Deny to all of those matched actions.- Consequently, this statement explicitly denies access to every AWS service across the entire platform except Amazon S3!
- For Amazon S3 actions, this statement does not apply. S3 actions remain unmentioned, which results in a Default (Implicit) Deny unless another statement explicitly allows them.
Comparison: NotAction with Allow vs. Deny
| Statement Elements | Practical Meaning & Impact | Common Valid Use Case |
|---|---|---|
Effect: Allow<br/>NotAction: iam:*<br/>Resource: * | Grants permissions to all AWS services except IAM. Any action with prefix iam: is not allowed by this statement. | Granting broad administrative capabilities to developers while preventing them from modifying IAM credentials or escalating privileges. |
Effect: Deny<br/>NotAction: s3:*<br/>Resource: * | Enforces an explicit Deny on all AWS services except S3. No other policy can ever grant access to EC2, RDS, DynamoDB, etc. | Lockdown or quarantine scenarios where an identity must be forcefully blocked from every AWS service except an isolated bucket. |
Effect: Deny<br/>Action: s3:*<br/>NotResource: arn:aws:s3:::audit-bucket/* | Denies all S3 actions except when targeting the specified audit bucket. | Restricting an identity so it cannot interact with any S3 bucket other than the designated corporate audit repository. |
Global Condition Keys for Zero-Trust DevOps Governance
Condition keys provide attribute-based access control (ABAC) and environmental constraints that must evaluate to true for a statement to take effect.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnforceSecureTransportAndSourceVPC",
"Effect": "Deny",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::corp-compliance-vault",
"arn:aws:s3:::corp-compliance-vault/*"
],
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
},
{
"Sid": "RestrictToCorporateVPC",
"Effect": "Deny",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::corp-compliance-vault",
"arn:aws:s3:::corp-compliance-vault/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpc": "vpc-0123456789abcdef0"
}
}
}
]
}
Critical Condition Keys for Enterprise Pipelines
aws:PrincipalArn/aws:PrincipalTag/${TagKey}: Validates the ARN or ABAC session tags of the caller. Used to enforce project-based access where a developer role with tagProject=Billingcan only manipulate resources taggedProject=Billing.aws:PrincipalOrgID: Validates that the calling principal belongs to a specific AWS Organization ID (e.g.,o-a1b2c3d4e5). Extremely valuable in S3 bucket policies, KMS key policies, and EventBridge bus policies to allow all current and future accounts in an organization without manual account ID enumeration.aws:SourceIp: Restricts requests to specific public IP CIDR ranges. Exam Trap:aws:SourceIpwill evaluate to null and fail if the call originates from an AWS service acting on behalf of the user (e.g., AWS CloudFormation deploying resources), unless combined withaws:ViaAWSService.aws:SourceVpc/aws:SourceVpce: Enforces that requests originate from a specific VPC ID or VPC Endpoint ID (via AWS PrivateLink), preventing data exfiltration over the public internet.aws:SecureTransport: A boolean condition key. When checked with"Bool": {"aws:SecureTransport": "false"}under an explicitDeny, it mandates that all incoming API requests utilize TLS (HTTPS) and rejects plain HTTP requests.aws:MultiFactorAuthPresent/aws:MultiFactorAuthAge: Enforces that the temporary session was authenticated using multi-factor authentication (MFA) and restricts the age of the MFA authentication event (e.g., under 3600 seconds) for sensitive operational actions such as deleting databases or terminating production instances.
An enterprise DevOps team manages a multi-account AWS environment. An application running on Amazon EC2 in Account A (111111111111) needs to read objects from an Amazon S3 bucket located in Account B (222222222222). The S3 bucket is encrypted using an AWS KMS customer-managed key (CMK) located in Account B. The EC2 instance assumes an IAM role in Account A that has an identity policy granting s3:GetObject on the Account B bucket and kms:Decrypt on the CMK ARN. The S3 bucket policy in Account B explicitly allows the Account A IAM role ARN to execute s3:GetObject. However, the KMS key policy in Account B contains only its default statement granting kms:* permissions to Account B's root principal. When the EC2 instance attempts to read an encrypted object from the bucket, it receives an Access Denied error. What is the root cause of this failure?
A junior cloud engineer attaches the following IAM policy statement to a developer IAM role in an AWS account: {"Effect": "Deny", "NotAction": "s3:", "Resource": ""}. The role also has an attached policy allowing ec2:DescribeInstances, rds:DescribeDBInstances, and s3:GetObject. How does the IAM policy evaluation engine evaluate requests when a developer authenticated with this role attempts to run ec2:DescribeInstances and s3:GetObject?
A DevOps engineer assumes an IAM role in an AWS account. The role has an Identity-Based Policy allowing ec2:* and s3:. The role is also constrained by an IAM Permissions Boundary that allows ec2: and dynamodb:. When assuming the role via the AWS STS AssumeRole API, the engineer passes an inline STS Session Policy that allows s3: and ec2:DescribeInstances. Which actions is the engineer authorized to execute during the temporary STS session?