AWS IAM Policy Evaluator

Paste your policies, name one API call, and get a straight answer with the statement that produced it. AWS's own simulator answers this and needs an account, credentials and the policies already attached. This needs a browser tab, so it works on a policy that does not exist yet.

The request

One API call: who is asking, what they want to do, and to what.

Condition context Values for keys like aws:SecureTransport. Leave one out and any statement needing it is reported as conditional rather than guessed.
Resource policy + Attached to the bucket, queue or key itself. Required for a cross-account call.
Service control policy + An organisation-level ceiling. If supplied, it has to allow the action or nothing else matters.
Permissions boundary + Also a ceiling. Effective permissions are the intersection of this and the identity policy.

    How it was decided

      Paste a policy to see every statement evaluated against this request.

      Examples

      Real requests you can load into the evaluator above. Each one is a rule that decides access questions, and most of them are the reason a call is denied when the identity policy plainly allows it.

      An explicit Deny wins

      Denied

      No Allow anywhere overrides it, however specific. This is the first rule of IAM evaluation and the answer to most access questions.

      Action
      s3:GetObject
      Resource
      arn:aws:s3:::my-bucket/reports/q1.pdf
      Principal
      arn:aws:iam::111122223333:role/App
      Accounts
      same account

      Identity policy

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "AllowS3", "Effect": "Allow", "Action": "s3:*", "Resource": "*" }
        ]
      }

      Resource policy

      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Sid": "DenyOutsideVpc",
            "Effect": "Deny",
            "Principal": "*",
            "Action": "s3:*",
            "Resource": "arn:aws:s3:::my-bucket/*"
          }
        ]
      }

      Cross-account needs both sides

      Denied

      The identity policy allows it and nothing on the resource does. Within one account either is enough; across accounts both are required.

      Action
      s3:GetObject
      Resource
      arn:aws:s3:::partner-bucket/feed.json
      Principal
      arn:aws:iam::111122223333:role/App
      Accounts
      different accounts

      Identity policy

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "AllowS3", "Effect": "Allow", "Action": "s3:*", "Resource": "*" }
        ]
      }

      A service control policy is a ceiling

      Denied

      The identity policy allows s3:*, and the SCP only allows ec2. An SCP grants nothing, so anything outside it is denied however permissive the identity policy is.

      Action
      s3:GetObject
      Resource
      arn:aws:s3:::my-bucket/reports/q1.pdf
      Principal
      arn:aws:iam::111122223333:role/App
      Accounts
      same account

      Identity policy

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "AllowS3", "Effect": "Allow", "Action": "s3:*", "Resource": "*" }
        ]
      }

      Service control policy

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "ComputeOnly", "Effect": "Allow", "Action": "ec2:*", "Resource": "*" }
        ]
      }

      A permissions boundary is a ceiling too

      Denied

      Effective permissions are the intersection of the boundary and the identity policy. The identity policy allows the write and the boundary only allows reads.

      Action
      s3:PutObject
      Resource
      arn:aws:s3:::my-bucket/uploads/new.pdf
      Principal
      arn:aws:iam::111122223333:role/App
      Accounts
      same account

      Identity policy

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "AllowS3", "Effect": "Allow", "Action": "s3:*", "Resource": "*" }
        ]
      }

      Permissions boundary

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "ReadOnly", "Effect": "Allow", "Action": "s3:Get*", "Resource": "*" }
        ]
      }

      A condition nobody supplied

      Conditional

      The statement matches and its condition cannot be evaluated, so the answer is neither allowed nor denied. Treating an unknown condition as satisfied is how a tool tells you that you are safe when you are not.

      Action
      s3:GetObject
      Resource
      arn:aws:s3:::my-bucket/reports/q1.pdf
      Principal
      arn:aws:iam::111122223333:role/App
      Accounts
      same account

      Identity policy

      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Sid": "MfaOnly",
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*",
            "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } }
          }
        ]
      }

      The shape that works across accounts

      Allowed

      The same cross-account call as above, with the bucket policy naming the calling role. Both sides allow it, so it is allowed.

      Action
      s3:GetObject
      Resource
      arn:aws:s3:::partner-bucket/feed.json
      Principal
      arn:aws:iam::111122223333:role/App
      Accounts
      different accounts

      Identity policy

      {
        "Version": "2012-10-17",
        "Statement": [
          { "Sid": "AllowS3", "Effect": "Allow", "Action": "s3:*", "Resource": "*" }
        ]
      }

      Resource policy

      {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Sid": "AllowPartner",
            "Effect": "Allow",
            "Principal": { "AWS": "arn:aws:iam::111122223333:role/App" },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::partner-bucket/*"
          }
        ]
      }

      Common mistakes

      These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

      1. Reading an Allow as the final answer

        Evaluation is: explicit Deny wins, then an SCP or permission boundary can remove it, then a resource policy can grant it. An Allow in the identity policy is one input of several.

        Instead:Check the boundaries and the resource policy too. A permission that works in one account can fail in another with the same identity policy.

      2. Forgetting the resource policy can grant on its own

        For S3, KMS and a few others, a resource policy can permit a principal with no matching identity policy at all. Cross-account access usually works this way.

        Instead:Evaluate both sides when the principal is in another account.

      3. Assuming NotAction means Deny

        NotAction in an Allow grants everything EXCEPT the listed actions, which is almost always wider than intended.

        Instead:Prefer listing actions explicitly. NotAction is for a small number of genuine exclusions.

      What it does

      Grading a policy is one question. Whether a specific call succeeds is a different and much harder one, and it is the one people actually have.

      The real precedence, not a guess

      An explicit Deny beats every Allow, in any policy, however specific, and that is the rule most people get backwards. A service control policy and a permissions boundary are ceilings that must each permit the call independently. What is left is the implicit deny everything starts from. The answer names the exact statement that decided it.

      Conditions are evaluated, or reported honestly

      Supply values for the context keys and the conditions are evaluated properly, including StringLike, ArnLike, Bool, numeric and date operators. Leave one out and the statement is reported as conditional rather than assumed satisfied. A tool that guesses here tells you that you are safe when you are not.

      Cross-account is a different question

      Within one account an Allow from either the identity policy or the resource policy is enough. Across accounts both are required, and a call failing for exactly that reason is the classic bucket-in-another-account puzzle. Untick the box and the evaluation changes accordingly.

      Every statement, with why it missed

      The trace shows each statement, whether the action, resource and principal matched, and which one failed. Turn on the non-matching statements and a policy that looked correct usually reveals a resource ARN that is one slash-star away from covering the object you asked about.

      How AWS decides whether a request is allowed

      Every API call is evaluated against every policy that applies to it, in a fixed order. Specificity plays no part. Once the order is clear, most 'why is this denied' questions answer themselves, and so do the more worrying 'why is this allowed' ones.

      The order, in full

      Evaluation starts from an implicit deny and works down. The first rule that produces a decision wins, and only the first rule can produce a Deny that nothing else can reverse. Note where the identity and resource policies sit: last, and only after both ceilings have already agreed.

      1. explicit Deny in any policy      -> DENIED, final
      2. service control policy            -> must Allow, or DENIED
      3. permissions boundary              -> must Allow, or DENIED
      4. identity or resource policy       -> Allow -> ALLOWED
      5. nothing matched                   -> DENIED (implicit)

      Specificity is irrelevant

      This is the assumption that causes the most confusion, because it holds in almost every other permission system. A Deny on Action "*" and Resource "*" beats an Allow naming one exact object ARN. There is no most-specific-wins tiebreak and no ordering within a document: a Deny anywhere in any attached policy ends the evaluation.

      Allow  s3:GetObject  on arn:aws:s3:::bucket/exact-file.csv
      Deny   s3:*          on *
      
      result: denied

      Ceilings do not grant anything

      A service control policy and a permissions boundary can only take permissions away. An SCP allowing Action "*" grants nobody anything: it merely declines to remove anything. Permissions still have to come from an identity or resource policy underneath. This catches people who attach a permissive SCP and expect access to appear.

      Within an account, either side is enough

      For a call inside one account, an Allow in the identity policy or in the resource policy is sufficient. Across accounts both are required: the caller's account must permit the call and the resource's account must permit the caller. A cross-account request that fails despite an obviously correct bucket policy is nearly always missing the identity half, and vice versa.

      same account       identity Allow  OR  resource Allow
      cross-account      identity Allow  AND resource Allow

      Conditions decide whether a statement applies at all

      A condition is not a separate check bolted on afterwards: a statement whose condition is not met simply does not apply, as though it were not there. That is why a Deny guarded by a condition is safe when the condition is false, and why the IfExists suffix matters so much: it makes the condition pass when the key is absent, which quietly turns a restriction into a no-op for any caller who does not send the key.

      "Condition": {
        "BoolIfExists": { "aws:MultiFactorAuthPresent": "false" }
      }
      
      fires when MFA is absent OR the key is missing entirely, which is not the same as "when MFA was not used"

      What this cannot see

      It reads the documents you paste. It does not know your account, so it cannot see which policies are actually attached, whether the role exists, who owns the resource, or what a session policy narrowed the call to on the way in.

      There are also service-specific paths that sit outside policy evaluation entirely: an S3 object ACL, a KMS grant, a VPC endpoint policy, a Lambda resource policy added by an event source. Any of those can permit a call that this evaluation shows as denied.

      The useful way to read a result: denied is reliable, because a Deny or a missing Allow in the documents you supplied really does end the call. Allowed means nothing in these documents prevents it, which is a strong statement about the policies and not a guarantee about the account.