Skip to main content

az-rbac-watch

CLI for Azure RBAC drift detection, governance guardrails, and change tracking.

What it does

Six focused commands in one tool:

  • discover — generate a baseline policy YAML from current RBAC assignments
  • scan — detect drift from your declared baseline
  • audit — detect forbidden patterns and governance violations
  • validate — check policy syntax offline
  • snapshot — capture current RBAC state as JSON
  • diff — compare snapshots to track changes over time

Neither OPA nor Azure Policy can natively scan Azure RBAC assignments. az-rbac-watch fills that gap with a CLI designed for local use, scheduled checks, and CI/CD pipelines.

Installation

# One-liner bootstrap (installs uv if needed, then puts az-rbac-watch on your PATH)
curl -fsSL https://raw.githubusercontent.com/maxvanp/az-rbac-watch/main/scripts/install.sh | sh

Manual install with uv:

uv tool install az-rbac-watch
uv tool update-shell

Fallback with pipx:

pipx install az-rbac-watch

The bootstrap script can install a compatible Python automatically through uv. You still need Azure CLI for authentication.

Shell completion

Enable tab completion for bash, zsh, or fish:

az-rbac-watch --install-completion

Restart your shell after installation.

Quick start

1. Authenticate

az login

2. Discover existing assignments

# Single subscription
az-rbac-watch discover -t <tenant-id> -s <subscription-id> -o my_policy.yaml

# All accessible scopes
az-rbac-watch discover -t <tenant-id> -o my_policy.yaml

3. Scan for drift (RBAC as Code)

The scan command compares actual RBAC assignments against your baseline rules. Any assignment not covered by a baseline rule is reported as DRIFT.

# Console output
az-rbac-watch scan -p my_policy.yaml

# HTML report
az-rbac-watch scan -p my_policy.yaml -o report.html

# JSON (CI/CD)
az-rbac-watch scan -p my_policy.yaml --format json

4. Audit guardrails (Policy as Code)

The audit command checks governance rules (forbidden patterns). Any assignment matching a governance rule is reported as a violation.

# Console output
az-rbac-watch audit -p my_policy.yaml

# HTML report
az-rbac-watch audit -p my_policy.yaml -o report.html

# JSON (CI/CD)
az-rbac-watch audit -p my_policy.yaml --format json

A starter kit with common governance rules is available in examples/deny_rules_starter.yaml.

4b. Framework compliance (CIS Benchmark)

# Audit with CIS Azure Foundations Benchmark mapping
az-rbac-watch audit -p my_policy.yaml --framework CIS -o compliance.html

# Console summary only
az-rbac-watch audit -p my_policy.yaml --framework CIS

5. Validate policy syntax (offline)

az-rbac-watch validate -p my_policy.yaml

6. Capture RBAC snapshots

# From a policy file
az-rbac-watch snapshot -p my_policy.yaml -o snapshot_2026-03-09.json

# From explicit scopes
az-rbac-watch snapshot -t <tenant-id> -s <subscription-id> -o snapshot.json

7. Compare snapshots (change tracking)

# Console output
az-rbac-watch diff snapshot_old.json snapshot_new.json

# JSON (CI/CD)
az-rbac-watch diff snapshot_old.json snapshot_new.json --format json -o changes.json

Core workflow

Use discover once to capture your current baseline, then run scan and audit continuously. Add snapshot and diff when you want explicit change tracking between two points in time.

Two axes, one tool

Axis Command Rule type Finding Question answered
RBAC as Code scan baseline DRIFT "Is there something I didn't declare?"
Policy as Code audit governance GOVERNANCE_VIOLATION "Is there something forbidden?"
Change tracking snapshot + diff n/a Added / Removed / Modified "What changed since last time?"
Framework compliance audit --framework governance GOVERNANCE_VIOLATION + score "How do we score against CIS?"

You can use both in the same policy file. Each command focuses on its rule type and ignores the other.

Scope modes

The policy model supports two scope modes:

  • scope: explicit (default) — only scans subscriptions and management groups listed in the YAML
  • scope: all — auto-discovers all accessible scopes at scan time, with optional exclusions
scope: all
exclude_subscriptions:
  - "22222222-2222-2222-2222-222222222222"
exclude_management_groups:
  - "mg-sandbox"

CLI exclusions (--exclude-subscription, --exclude-management-group) apply on top of YAML exclusions.

CLI reference

az-rbac-watch scan

Detects RBAC drift — compares actual state against baseline rules.

Option Description
-p, --policy PATH Policy model YAML (required)
-t, --tenant-id ID Tenant ID (ad-hoc mode)
-s, --subscription ID Subscription to scan, repeatable (ad-hoc mode)
-m, --management-group ID Management group to scan, repeatable (ad-hoc mode)
-o, --output PATH HTML report output path
-f, --format FORMAT console (default) or json
--orphans-only Scan only for orphaned assignments (requires --tenant-id)
--dry-run Show scan plan without making API calls
--exclude-subscription ID Exclude subscription (repeatable)
--exclude-management-group ID Exclude management group (repeatable)
-v, --verbose Debug logging
--debug Show full traceback on error

az-rbac-watch audit

Checks governance guardrails — evaluates governance rules against actual state.

Option Description
-p, --policy PATH Policy model YAML (required)
-t, --tenant-id ID Tenant ID (ad-hoc mode)
-s, --subscription ID Subscription to scan, repeatable (ad-hoc mode)
-m, --management-group ID Management group to scan, repeatable (ad-hoc mode)
-o, --output PATH HTML report output path
-f, --format FORMAT console (default) or json
--dry-run Show scan plan without making API calls
--framework NAME Map findings to compliance framework (e.g. 'CIS' or path to YAML)
--exclude-subscription ID Exclude subscription (repeatable)
--exclude-management-group ID Exclude management group (repeatable)
-v, --verbose Debug logging
--debug Show full traceback on error

az-rbac-watch discover

Option Description
-t, --tenant-id ID Tenant ID
-s, --subscription ID Subscription to scan (repeatable)
-m, --management-group ID Management group to scan (repeatable)
-o, --output PATH Output YAML (default: discovered_policy.yaml)

az-rbac-watch validate

Option Description
-p, --policy PATH Policy model YAML to validate (required)

az-rbac-watch snapshot

Captures a full RBAC snapshot (assignments + role definitions) as JSON.

Option Description
-p, --policy PATH Policy model YAML (uses its scopes)
-t, --tenant-id ID Tenant ID
-s, --subscription ID Subscription to scan (repeatable)
-m, --management-group ID Management group to scan (repeatable)
--exclude-subscription ID Exclude subscription (repeatable)
--exclude-management-group ID Exclude management group (repeatable)
-o, --output PATH Output JSON file (required)
-v, --verbose Debug logging
--debug Show full traceback on error

az-rbac-watch diff

Compares two snapshots and shows RBAC changes (added, removed, modified assignments).

Option Description
OLD_SNAPSHOT Path to the older snapshot JSON file (required)
NEW_SNAPSHOT Path to the newer snapshot JSON file (required)
-f, --format FORMAT console (default), json, or html
-o, --output PATH Output file path

HTML format is auto-detected when --output ends with .html.

Exit codes

Code Meaning
0 Compliant — no findings detected
1 Non-compliant — findings detected
2 Error — authentication failure, API error, invalid YAML

The diff command returns 0 for no changes, 1 for changes detected.

Example CI/CD usage:

# Check drift
az-rbac-watch scan -p policy.yaml --format json -o drift.json
scan_exit=$?

# Check guardrails
az-rbac-watch audit -p policy.yaml --format json -o audit.json
audit_exit=$?

if [ "$scan_exit" -eq 1 ] || [ "$audit_exit" -eq 1 ]; then
  echo "Non-compliant — review reports"
elif [ "$scan_exit" -eq 2 ] || [ "$audit_exit" -eq 2 ]; then
  echo "Scan error — check credentials and permissions"
fi

Troubleshooting

Authentication error

  • Run az login to refresh your credentials
  • Verify DefaultAzureCredential can authenticate (check AZURE_* env vars or managed identity)

Access denied

  • The scanning principal needs Microsoft.Authorization/roleAssignments/read on each scoped subscription/MG
  • Check with: az role assignment list --scope /subscriptions/<id> --assignee <principal-id>

Names not resolved (UUIDs shown)

  • The App Registration (or user) needs Directory.Read.All permission on Microsoft Graph
  • Run with --verbose to see Graph API errors in the logs

Throttling

  • Azure ARM API rate-limits parallel requests
  • Reduce parallelism: the default is 4 workers (configurable in code via max_workers)
  • Wait a few minutes and retry

Full traceback

  • Use --debug to see the complete Python traceback on any error

GitHub Actions

Use the composite action to integrate az-rbac-watch into your CI/CD pipelines.

Basic usage

- uses: maxvanp/az-rbac-watch@v0.5.0
  with:
    mode: scan
    policy: policy.yaml
    format: json
    output: report.json

Available modes

Mode Description Azure auth required
scan Detect RBAC drift against baseline rules Yes
audit Check governance guardrails Yes
validate Validate policy YAML syntax (offline) No
snapshot Capture RBAC snapshot Yes
diff Compare two snapshots No

Example workflows

Ready-to-use workflow templates are available in examples/workflows/:

Copy the desired workflow to .github/workflows/ in your repository and configure the required secrets.

Inputs

Input Required Default Description
mode yes — Command: scan, audit, validate, snapshot, diff
policy no — Path to policy YAML file
tenant-id no — Azure tenant ID
subscriptions no — Subscription IDs (comma-separated)
management-groups no — Management group IDs (comma-separated)
format no console Output format: console or json
output no — Output file path
old-snapshot no — Old snapshot path (diff mode)
new-snapshot no — New snapshot path (diff mode)
python-version no 3.12 Python version
extra-args no — Additional CLI arguments

Outputs

Output Description
exit-code 0 = compliant, 1 = findings/changes, 2 = error
report-path Path to generated report (if output is set)

Rule match operators

All comparisons are case-insensitive. Conditions are combined with AND logic.

Operator Type Description
scope str Exact scope match
scope_prefix str Scope starts with value
role str Exact role name
role_in list Role is in list
role_not_in list Role is NOT in list
role_type str BuiltInRole or CustomRole
principal_type str User, Group, or ServicePrincipal
principal_type_in list Principal type is in list
principal_id str Exact principal ID
principal_name_prefix str Display name starts with
principal_name_not_prefix str Display name does NOT start with
principal_name_contains str Display name contains
principal_name_not_contains str Display name does NOT contain

Name-based operators require Graph API access. If unavailable, they evaluate to false (no false positives).

Required Azure permissions

  • Microsoft.Authorization/roleAssignments/read on scanned scopes
  • Directory.Read.All (Graph API, for display name resolution)

License

MIT

Metadata

Release files for az-rbac-watch 0.10.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for az-rbac-watch 0.10.1
File Size Uploaded
az_rbac_watch-0.10.1.tar.gz 238.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for az-rbac-watch 0.10.1
File Interpreter ABI Platform
az_rbac_watch-0.10.1-py3-none-any.whl Python 3 none any Details

Total release size: 304.7 kB

Release files / az_rbac_watch-0.10.1.tar.gz

Download URL az_rbac_watch-0.10.1.tar.gz
Size 238.4 kB
Tags Source
SHA-256 checksum
How to use checksums
451f1d1e85fd933dbcef88fc654e6aaad5ad9d4d608ce3e4d804a865e928ba19
BLAKE2b-256 checksum
How to use checksums
83969737294d6aafcb2b17348412c63425ce3c3bc7f9055e4ae5df870e647ca8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 11, 2026.

Transparency log

Release files / az_rbac_watch-0.10.1-py3-none-any.whl

Download URL az_rbac_watch-0.10.1-py3-none-any.whl
Size 66.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
842684cadd55fa0c78eec46f7ac71f57e367090181320d637cc32d9c6977d777
BLAKE2b-256 checksum
How to use checksums
01b36984e75b940743923cbc6b5e04dc262d5b2737b0ed6968131235ab3867c6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.10.1 This release

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page