Skip to content

GitHub Actions Integration

IAM Policy Validator provides a native GitHub Action for seamless CI/CD integration with PR comments, code scanning, and AWS Access Analyzer support.

Quick Start

name: Validate IAM Policies

on:
  pull_request:
    paths:
      - "**.json"
      - "**.yaml"

jobs:
  validate:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - uses: boogy/iam-policy-validator@v1
        with:
          path: ./policies/

Action Inputs

Core Inputs

Input Description Default
path Path(s) to IAM policy files or directories. Newline- or space-separated for multiple paths Required
config-file Path to custom configuration file (iam-validator.yaml) Auto-detect
policy-type Policy type applied to every policy: IDENTITY_POLICY, RESOURCE_POLICY, TRUST_POLICY, SERVICE_CONTROL_POLICY, RESOURCE_CONTROL_POLICY. Empty resolves each file via config policy_types: globs, then content auto-detection auto-detect
recursive Recursively search directories for policy files true
fail-on-warnings Fail validation if warnings are found (default: only fail on errors) false

GitHub Integration

Input Description Default
post-comment Post validation results as PR comment true
create-review Create line-specific review comments on PR files true
allow-owner-ignore Allow CODEOWNERS to ignore findings by replying 'ignore' true
github-summary Write summary to GitHub Actions job summary false
github-token GitHub token for posting comments and reviews ${{ github.token }}
comment-tag Run scope tag (1-32 chars, [A-Za-z0-9._-]) for parallel runs on the same PR (unset)

Output Options

Input Description Default
format Output format: console, enhanced, json, markdown, sarif, csv, html console
output-file Path to save output file (for json, markdown, sarif, csv, html formats) -
upload-sarif Upload SARIF results to GitHub Code Scanning false
show-console-output Show enhanced validation results in job logs true
summary Show Executive Summary section in enhanced output false
severity-breakdown Show Issue Severity Breakdown section in enhanced output false

Performance Options

Input Description Default
stream Process files one-by-one (memory efficient for large repos) false
batch-size Number of policies to process per batch when streaming 10
aws-services-dir Path to pre-downloaded AWS service definitions (offline mode) -
custom-checks-dir Path to directory containing custom validation checks -
log-level Logging level: debug, info, warning, error, critical warning

AWS Access Analyzer

Input Description Default
use-access-analyzer Use AWS IAM Access Analyzer for validation false
access-analyzer-region AWS region for Access Analyzer us-east-1
run-all-checks Run custom checks after Access Analyzer passes false
check-access-not-granted Actions that should NOT be granted (space-separated) -
check-access-resources Resources to check with check-access-not-granted -
check-no-new-access Path to baseline policy for new access comparison -
check-no-public-access Check that resource policies don't allow public access false
public-access-resource-type Resource type(s) for public access check AWS::S3::Bucket

Action Outputs

Output Description
validation-result Validation result (success or failure)
total-policies Total number of policies validated
valid-policies Number of valid policies
invalid-policies Number of invalid policies
total-issues Total number of issues found

Examples

Basic Validation with PR Comments

name: IAM Policy Validation

on:
  pull_request:
    paths:
      - "policies/**"
      - "terraform/**/*.json"

permissions:
  contents: read
  pull-requests: write

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate IAM Policies
        uses: boogy/iam-policy-validator@v1
        with:
          path: ./policies/
          config-file: ./iam-validator.yaml
          post-comment: true
          create-review: true
          github-summary: true

GitHub Code Scanning with SARIF

Upload results to GitHub's Security tab for centralized vulnerability tracking:

name: IAM Policy Security Scan

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

permissions:
  contents: read
  security-events: write # Required for SARIF upload

jobs:
  security-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate IAM Policies
        uses: boogy/iam-policy-validator@v1
        with:
          path: ./policies/
          format: sarif
          output-file: iam-results.sarif
          upload-sarif: true

Results appear in your repository's Security > Code scanning alerts tab.

AWS Access Analyzer Integration

Use AWS IAM Access Analyzer for additional validation (requires AWS credentials):

name: IAM Policy Analysis

on:
  pull_request:
    paths:
      - "policies/**"

permissions:
  contents: read
  pull-requests: write
  id-token: write # For OIDC authentication

jobs:
  analyze:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
          aws-region: us-east-1

      - name: Analyze with Access Analyzer
        uses: boogy/iam-policy-validator@v1
        with:
          path: ./policies/
          use-access-analyzer: true
          access-analyzer-region: us-east-1
          run-all-checks: true # Also run built-in checks

Check for Prohibited Actions

Ensure specific dangerous actions are never granted:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    use-access-analyzer: true
    check-access-not-granted: >-
      iam:CreateAccessKey
      iam:CreateUser
      iam:AttachUserPolicy
      s3:DeleteBucket
      kms:ScheduleKeyDeletion
    check-access-resources: >-
      arn:aws:iam::*:user/*
      arn:aws:s3:::prod-*
      arn:aws:kms:*:*:key/*

Check for Public Access (Resource Policies)

Validate S3 bucket policies don't allow public access:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./bucket-policies/
    policy-type: RESOURCE_POLICY
    use-access-analyzer: true
    check-no-public-access: true
    public-access-resource-type: AWS::S3::Bucket AWS::Lambda::Function

Multiple Policy Paths

Validate policies from different directories:

- uses: boogy/iam-policy-validator@v1
  with:
    path: |
      ./iam-policies/
      ./terraform/modules/iam/
      ./cloudformation/policies/

Different Policy Types (Matrix Strategy)

Validate different policy types in parallel. Pass a unique comment-tag per matrix entry so each run posts its own PR summary, review thread, and ignored-findings store instead of overwriting siblings (issue #103).

name: Validate All Policy Types

on: [pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    strategy:
      matrix:
        include:
          - path: ./identity-policies/
            type: IDENTITY_POLICY
            tag: identity
          - path: ./trust-policies/
            type: TRUST_POLICY
            tag: trust
          - path: ./resource-policies/
            type: RESOURCE_POLICY
            tag: resource
          - path: ./scps/
            type: SERVICE_CONTROL_POLICY
            tag: scp
    steps:
      - uses: actions/checkout@v4
      - uses: boogy/iam-policy-validator@v1
        with:
          path: ${{ matrix.path }}
          policy-type: ${{ matrix.type }}
          comment-tag: ${{ matrix.tag }}

Why comment-tag is required for parallel runs

The validator updates the same canonical PR comment on every run so repeated pushes don't spam the timeline. Without a comment-tag, every parallel matrix job targets the same comment — the second job overwrites the first. Set a stable, distinct tag per scope (identity, trust, ${{ matrix.tag }}, ${{ github.job }}) and each job updates its own thread on subsequent runs.

Tags must match [A-Za-z0-9._-]{1,32}.

Large Repositories (Streaming Mode)

For repositories with many policy files, use streaming mode to reduce memory usage:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    stream: true
    batch-size: 20 # Process 20 policies at a time

Custom Checks

Use organization-specific validation rules:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    custom-checks-dir: ./custom-checks/
    config-file: ./iam-validator.yaml

Never combine custom checks with pull_request_target

custom-checks-dir executes every .py file in the directory. On pull_request_target (or any workflow that checks out untrusted fork code with elevated permissions), an attacker's PR can modify those files and run arbitrary code with your workflow's token. Only enable custom checks on trusted refs.

Offline Mode (Air-Gapped Environments)

Pre-download AWS service definitions for environments without internet access:

# First, generate the service definitions (run once, commit to repo)
# iam-validator sync-services --output-dir ./aws-services/

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    aws-services-dir: ./aws-services/

Using Outputs in Workflow

Access validation results for conditional logic:

jobs:
  validate:
    runs-on: ubuntu-latest
    outputs:
      result: ${{ steps.validate.outputs.validation-result }}
      issues: ${{ steps.validate.outputs.total-issues }}
    steps:
      - uses: actions/checkout@v4
      - id: validate
        uses: boogy/iam-policy-validator@v1
        with:
          path: ./policies/

  notify:
    needs: validate
    if: needs.validate.outputs.result == 'failure'
    runs-on: ubuntu-latest
    steps:
      - name: Send Slack notification
        run: |
          echo "Validation failed with ${{ needs.validate.outputs.issues }} issues"
          # Add your notification logic here

Fail on Specific Severities

Configure which severity levels cause the workflow to fail:

# Fail on errors and critical issues only (default)
- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/

# Fail on warnings too
- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    fail-on-warnings: true

Multiple Output Formats

Generate reports in multiple formats:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    format: json
    output-file: validation-report.json
    github-summary: true

- name: Upload Report
  uses: actions/upload-artifact@v4
  with:
    name: iam-validation-report
    path: validation-report.json

PR Comments and Reviews

Inline Review Comments

When create-review: true, the action creates line-specific comments on the exact locations where issues are found in the PR diff.

Summary Comment

A summary comment is posted showing:

  • Total policies validated
  • Issues grouped by severity
  • Links to specific findings

CODEOWNERS Ignore

When allow-owner-ignore: true, code owners can reply "ignore" to dismiss specific findings. This is useful for acknowledged exceptions.

Ignore records live in a hidden PR comment and are reconciled on every run: once a finding stops being reported — the policy was fixed — its record is dropped, so the summary's Ignored Findings count and table only ever describe live findings. Reconciliation is scoped to the files a run actually validated, so a run driven by a changed-files list cannot discard ignores belonging to policies it never looked at.

Each record is also checked against the reply that requested it. An ignore is trusted because an authorized user wrote it in a specific comment, so the record is dropped if that comment is gone — deleting your "ignore" reply revokes the ignore — or if it now belongs to someone other than the recorded user. That is what stops a hand-edited storage comment from silencing findings under another user's name. Records created before reply tracking have nothing to verify against and are left alone.

If the review-comment listing cannot be fetched, verification is skipped for that run rather than treating every reply as deleted, so a transient API failure never revokes a valid ignore.


Job Summary

Enable github-summary: true to add a summary to the Actions UI:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    github-summary: true
    summary: true
    severity-breakdown: true

The summary shows:

  • Validation status (pass/fail)
  • Total policies validated
  • Issue counts by severity
  • Executive summary (with summary: true)
  • Severity breakdown chart (with severity-breakdown: true)

Complete Production Example

A comprehensive workflow for production use:

name: IAM Policy Validation

on:
  pull_request:
    paths:
      - "policies/**"
      - "terraform/**/*.json"
      - "iam-validator.yaml"
  push:
    branches: [main]
    paths:
      - "policies/**"

permissions:
  contents: read
  pull-requests: write
  security-events: write # Required for SARIF upload

jobs:
  validate:
    name: Validate IAM Policies
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Validate Policies
        uses: boogy/iam-policy-validator@v1
        with:
          path: |
            ./policies/
            ./terraform/iam/
          config-file: ./iam-validator.yaml
          policy-type: IDENTITY_POLICY
          post-comment: true
          create-review: true
          github-summary: true
          summary: true
          severity-breakdown: true
          format: sarif
          output-file: iam-results.sarif
          upload-sarif: true # Uploads to GitHub Code Scanning

Troubleshooting

PR Comments Not Appearing

Ensure you have the correct permissions:

permissions:
  contents: read
  pull-requests: write

SARIF Upload Failing

Ensure you have security-events write permission:

permissions:
  security-events: write

Rate Limiting with Large Repos

Use streaming mode and increase batch size:

- uses: boogy/iam-policy-validator@v1
  with:
    path: ./policies/
    stream: true
    batch-size: 50

AWS Access Analyzer Permissions

Ensure your IAM role has:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "access-analyzer:ValidatePolicy",
        "access-analyzer:CheckAccessNotGranted",
        "access-analyzer:CheckNoNewAccess",
        "access-analyzer:CheckNoPublicAccess"
      ],
      "Resource": "*"
    }
  ]
}