-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started
This guide teaches you the fundamentals of IAM policy testing with politest through practical examples.
- Core Concepts
- Your First Test
- Understanding Test Output
- Testing Denied Actions
- Testing Multiple Actions
- Common Patterns
- Next Steps
politest uses the AWS SimulateCustomPolicy API to evaluate policy documents without deploying them. This means you can:
- Test policies before applying them to users/roles
- Validate policy changes won't break existing permissions
- Ensure policies grant only intended permissions
- Catch misconfigurations early in development
- Policy Document: The IAM policy you want to test (JSON format)
- Test Scenario: YAML file defining what to test (actions, resources, expectations)
-
Test Assertions: Expected outcomes (
allowed,implicitDeny,explicitDeny)
-
allowed: The action is permitted by the policy -
implicitDeny: The action is not explicitly allowed (default deny) -
explicitDeny: The action is explicitly denied in a policy statement
Let's create a simple S3 read-only policy and test it.
Create s3-readonly-policy.json:
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "S3ReadOnly",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": "*"
}]
}This policy allows reading S3 objects and listing buckets, but nothing else.
Create test-s3-readonly.yml:
# Reference the policy to test
policy_json: "s3-readonly-policy.json"
# Define test cases
tests:
- name: "Reading objects should be allowed"
action: "s3:GetObject"
resource: "arn:aws:s3:::my-bucket/data.txt"
expect: "allowed"
- name: "Listing buckets should be allowed"
action: "s3:ListBucket"
resource: "arn:aws:s3:::my-bucket"
expect: "allowed"
- name: "Writing objects should be denied"
action: "s3:PutObject"
resource: "arn:aws:s3:::my-bucket/data.txt"
expect: "implicitDeny"
- name: "Deleting objects should be denied"
action: "s3:DeleteObject"
resource: "arn:aws:s3:::my-bucket/data.txt"
expect: "implicitDeny"politest --scenario test-s3-readonly.ymlOutput:
Running 4 test(s)...
[1/4] Reading objects should be allowed
✓ PASS: allowed (matched: PolicyInputList.1)
[2/4] Listing buckets should be allowed
✓ PASS: allowed (matched: PolicyInputList.1)
[3/4] Writing objects should be denied
✓ PASS: implicitDeny
[4/4] Deleting objects should be denied
✓ PASS: implicitDeny
========================================
Test Results: 4 passed, 0 failed
========================================
Congratulations! You've successfully tested an IAM policy.
Let's break down what each part of the output means:
[1/4] Reading objects should be allowed
✓ PASS: allowed (matched: PolicyInputList.1)
-
[1/4]: Test number (1 out of 4 total) -
Reading objects should be allowed: Your test name -
✓ PASS: Test passed -
allowed: The actual decision from AWS -
(matched: PolicyInputList.1): Which policy statement allowed the action (statement 1 in the identity policy)
If a test fails, you'll see:
[3/4] Writing objects should be denied
✗ FAIL: expected implicitDeny, got allowed (matched: PolicyInputList.1)
This means:
- You expected the action to be denied (
implicitDeny) - But AWS actually allowed it (
allowed) - The action was allowed by statement 1 in your policy
This indicates your policy is too permissive!
There are two types of denies in IAM:
When an action isn't mentioned in any Allow statement:
tests:
- name: "EC2 actions not in policy should be implicitly denied"
action: "ec2:RunInstances"
resource: "*"
expect: "implicitDeny"When an action is in a Deny statement:
Policy with explicit deny:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
},
{
"Effect": "Deny",
"Action": "s3:DeleteBucket",
"Resource": "*"
}
]
}Test:
tests:
- name: "S3 read allowed"
action: "s3:GetObject"
resource: "arn:aws:s3:::bucket/*"
expect: "allowed"
- name: "Delete bucket explicitly denied"
action: "s3:DeleteBucket"
resource: "arn:aws:s3:::bucket"
expect: "explicitDeny"Key difference: explicitDeny always wins, even if there's an Allow statement!
For testing the same resource with multiple actions:
policy_json: "policy.json"
# Actions to test
actions:
- "s3:GetObject"
- "s3:PutObject"
- "s3:DeleteObject"
# Resources to test against
resources:
- "arn:aws:s3:::my-bucket/*"
# Expected outcomes for each action
expect:
"s3:GetObject": "allowed"
"s3:PutObject": "allowed"
"s3:DeleteObject": "implicitDeny"This is equivalent to running 3 separate tests (one per action).
For more complex scenarios with different resources per test:
policy_json: "policy.json"
tests:
- name: "Read from prod bucket"
action: "s3:GetObject"
resource: "arn:aws:s3:::prod-bucket/*"
expect: "allowed"
- name: "Write to dev bucket"
action: "s3:PutObject"
resource: "arn:aws:s3:::dev-bucket/*"
expect: "allowed"
- name: "Delete from prod bucket (should fail)"
action: "s3:DeleteObject"
resource: "arn:aws:s3:::prod-bucket/*"
expect: "implicitDeny"When to use each:
- Legacy format: Simple tests, same resource, multiple actions
- Collection format: Complex tests, descriptive names, different resources
See Scenario Formats for detailed comparison.
Policy limiting actions to specific buckets:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::dev-*",
"arn:aws:s3:::dev-*/*"
]
}]
}Test:
policy_json: "dev-only-policy.json"
tests:
- name: "Access to dev bucket allowed"
action: "s3:GetObject"
resource: "arn:aws:s3:::dev-data/file.txt"
expect: "allowed"
- name: "Access to prod bucket denied"
action: "s3:GetObject"
resource: "arn:aws:s3:::prod-data/file.txt"
expect: "implicitDeny"Policy allowing only read operations:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket"
],
"Resource": "*"
}]
}Test:
policy_json: "read-only-policy.json"
tests:
- name: "Read operations allowed"
action: "s3:GetObject"
resource: "arn:aws:s3:::any-bucket/*"
expect: "allowed"
- name: "Write operations denied"
action: "s3:PutObject"
resource: "arn:aws:s3:::any-bucket/*"
expect: "implicitDeny"
- name: "Delete operations denied"
action: "s3:DeleteObject"
resource: "arn:aws:s3:::any-bucket/*"
expect: "implicitDeny"Policy with wildcards:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}]
}Test to verify wildcards work as expected:
policy_json: "s3-admin-policy.json"
tests:
- name: "All S3 actions should be allowed"
action: "s3:GetObject"
resource: "arn:aws:s3:::bucket1/*"
expect: "allowed"
- action: "s3:PutObject"
resource: "arn:aws:s3:::bucket2/*"
expect: "allowed"
- action: "s3:DeleteBucket"
resource: "arn:aws:s3:::bucket3"
expect: "allowed"
- name: "Non-S3 actions still denied"
action: "ec2:RunInstances"
resource: "*"
expect: "implicitDeny"politest --scenario test.yml --save /tmp/response.jsonThis saves the full AWS API response, including:
- Which policy statements matched
- Evaluation details for each test
- Missing dependencies
Example response:
{
"EvaluationResults": [{
"EvalActionName": "s3:GetObject",
"EvalResourceName": "arn:aws:s3:::bucket/*",
"EvalDecision": "allowed",
"MatchedStatements": [{
"SourcePolicyId": "PolicyInputList.1",
"SourcePolicyType": "IAM Policy",
"StartPosition": { "Line": 4, "Column": 5 },
"EndPosition": { "Line": 9, "Column": 6 }
}]
}]
}Skip failing on mismatches to see all test outcomes:
politest --scenario test.yml --no-assertUseful for:
- Exploring what a policy actually allows
- Updating test expectations
- Debugging complex policy interactions
Now that you understand the basics:
- Learn Scenario Formats - Understand legacy vs collection format
- Use Template Variables - Make tests environment-agnostic
- Test Resource Policies - Cross-account S3, KMS, etc.
- Add SCPs and RCPs - Test organizational policies
- Test Conditions - IP restrictions, MFA, tags, etc.
Need more examples? Check out the test/scenarios/ directory for 18+ real examples →