How to Evaluate IAM Policy Allow vs Deny in Python
Evaluate an AWS-style IAM policy dict with explicit deny overriding allow and default deny.
Python code
36 linesimport json
def evaluate_policy(action, resource, policy):
"""Evaluate an IAM-like policy dict.
Explicit deny wins over allow. Default is deny.
"""
for statement in policy.get("Statement", []):
effect = statement.get("Effect")
actions = statement.get("Action", [])
resources = statement.get("Resource", [])
action_match = action in actions
resource_match = resource in resources
if action_match and resource_match:
if effect == "Deny":
return "deny"
if effect == "Allow":
decision = "allow"
return decision if "decision" in locals() else "deny"
if __name__ == "__main__":
iam_policy = {
"Statement": [
{"Effect": "Allow", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::demo-bucket/*"]},
{"Effect": "Deny", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::demo-bucket/secret/*"]},
]
}
cases = [
("s3:GetObject", "arn:aws:s3:::demo-bucket/report.txt"),
("s3:GetObject", "arn:aws:s3:::demo-bucket/secret/keys.txt"),
("s3:GetObject", "arn:aws:s3:::demo-bucket/report.txt"),
]
for action, resource in cases:
result = evaluate_policy(action, resource, iam_policy)
print(f"{action} on {resource} -> {result}")
Output
s3:GetObject on arn:aws:s3:::demo-bucket/report.txt -> allow
s3:GetObject on arn:aws:s3:::demo-bucket/secret/keys.txt -> deny
s3:GetObject on arn:aws:s3:::demo-bucket/report.txt -> allow
How it works
The evaluate_policy function iterates through each statement in the policy's Statement list. It checks if both the action and resource match the statement's Action and Resource arrays. An explicit Deny effect returns immediately, ensuring deny wins over any prior allow. If an Allow matches, the decision is saved but not returned immediately. At the end, if no deny matched, the last allow or the default deny is returned. The locals() check safely handles the case where no allow was found.
Common mistakes
- Using `in` with a string for Action/Resource when the policy may use a single string instead of a list
- Returning an allow on the first match without checking for later deny statements
- Forgetting that IAM policies can have wildcards like `s3:*` which this simple check does not handle
Variations
- Use a normalized comparison that expands wildcards using `fnmatch` to support `*` patterns in actions/resources.
- Add a condition block evaluation to check for `StringEquals` or other IAM condition operators.
Real-world use cases
- Unit-testing IAM policy logic in CI pipelines before deploying to AWS.
- Simulating access decisions for a custom authorization service that mirrors AWS IAM semantics.
- Building a security linter that flags overly permissive policies in infrastructure-as-code repositories.
Sponsored
More from Cloud + Python
- Build a URL Shortener Client with Python medium
- Create a Cloud Storage Helper Class in Python easy
- Create a Data Helper Class for Beginners in Python easy
- Cross Account Role Chaining Mock Credentials in Python medium
- Exponential Backoff with Jitter for Cloud API Calls in Python medium
- Generate Mock CloudFormation Stack Events in Python easy
Keep learning
Related tutorials and quizzes for this topic.