Skip to main content

Azure RBAC compliance monitoring — detect permission drift and governance violations

Project description

Azure Permissions Watch

Azure RBAC as Code — drift detection and guardrails.

What it does

Two complementary commands in one tool:

  • scanRBAC as Code (affirmative): declare your desired RBAC state in YAML, detect drift from that state
  • auditPolicy as Code (negative): define forbidden patterns (guardrails), detect violations

Both commands share the same RBAC scanner — the differentiating value is that neither OPA nor Azure Policy can natively scan RBAC assignments.

Installation

# Recommended — isolated install for CLI tools
pipx install az-rbac-watch

# Or with pip
pip install az-rbac-watch

Requires Python ≥ 3.12 and 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.

5. Validate policy syntax (offline)

az-rbac-watch validate -p my_policy.yaml

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?"

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)
-o, --output PATH HTML report output path
-f, --format FORMAT console (default) or json
--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)
-o, --output PATH HTML report output path
-f, --format FORMAT console (default) or json
--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)

Exit codes

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

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

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

az_rbac_watch-0.3.0.tar.gz (181.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

az_rbac_watch-0.3.0-py3-none-any.whl (40.6 kB view details)

Uploaded Python 3

File details

Details for the file az_rbac_watch-0.3.0.tar.gz.

File metadata

  • Download URL: az_rbac_watch-0.3.0.tar.gz
  • Upload date:
  • Size: 181.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for az_rbac_watch-0.3.0.tar.gz
Algorithm Hash digest
SHA256 807c6b34fcf0908de8633409a92e6000e29c74f55f35ae40d06cd47ae279e7e9
MD5 3d74e3dc31b2ecd6ffad7504a10f22d5
BLAKE2b-256 0dd23e43870885336c4ea2c8406c4f870af76c934195282deccba6130723018a

See more details on using hashes here.

Provenance

The following attestation bundles were made for az_rbac_watch-0.3.0.tar.gz:

Publisher: publish.yml on maxvanp/az-rbac-watch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file az_rbac_watch-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: az_rbac_watch-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 40.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for az_rbac_watch-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6f7097d0ed62f11167c7e5e2bc34ef29d13991ab9b9fcf827a0b3d7902cdb7f4
MD5 689b3a8587b6c88c20ad54c5897fe3b5
BLAKE2b-256 e729f1c61ec64b91744d62def9873812485c05bf59d669c5cd271eb7ee44b58e

See more details on using hashes here.

Provenance

The following attestation bundles were made for az_rbac_watch-0.3.0-py3-none-any.whl:

Publisher: publish.yml on maxvanp/az-rbac-watch

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page