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.
Last updated: September 2026

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:

  1. 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.
  2. Explicit Deny Precedence: An explicit Deny in any policy that applies to the request immediately supersedes any and all explicit Allow statements. If a single applicable policy evaluates to Deny, the final authorization decision is unconditionally Deny.
  3. Explicit Allow Requirement: In the absence of an explicit Deny, there must be at least one explicit Allow in 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 TypeAttachment PointScope & FunctionEvaluation Behavior
Service Control Policy (SCP)AWS Organizations Root, OU, or AccountMaximum entitlement guardrail across an entire accountMust allow the action; does not grant permissions to identities directly
Resource-Based PolicyAWS Resources (S3 buckets, KMS keys, SQS queues, Secrets)Specifies which principals can access the specific resourceCan grant access directly across accounts without role assumption
IAM Permissions BoundaryIAM Users or IAM RolesMaximum entitlement ceiling for an identityRestricts identity-based policies; effective permission is Identity ∩ Boundary
STS Session PolicyTemporary STS credentials (AssumeRole, GetFederationToken)Ephemeral permission filter passed dynamically during role assumptionRestricts assumed session; effective permission is Role ∩ SessionPolicy
Identity-Based PolicyIAM Users, IAM Groups, or IAM RolesGrants permissions directly to the authenticated identityEvaluated 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:

Effective Permissions=Identity PolicyPermissions BoundarySession PolicySCP\text{Effective Permissions} = \text{Identity Policy} \cap \text{Permissions Boundary} \cap \text{Session Policy} \cap \text{SCP}

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 Allow is 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: Deny applies 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 ElementsPractical Meaning & ImpactCommon 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 tag Project=Billing can only manipulate resources tagged Project=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:SourceIp will 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 with aws: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 explicit Deny, 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.
Loading diagram...
Comprehensive IAM Policy Evaluation Flowchart
Test Your Knowledge

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
B
C
D
Test Your Knowledge

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
B
C
D
Test Your Knowledge

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?

A
B
C
D