Skip to main content

az-costguard

Self-hosted Azure cost optimization scanner. Runs 204 rules across 12 FinOps domains — waste elimination, rightsizing, networking, storage, monitoring, and more — and reports findings with estimated monthly savings.


Prerequisites

Requirement Version
Python 3.11+
Azure CLI Any (for az login)
Azure RBAC Reader on target subscriptions

Required Azure Permissions

The minimum IAM role is Reader assigned at the subscription scope (or management group for multi-subscription scans). No write permissions are required — az-costguard is read-only.

The following Azure APIs are called during a scan:

API Used for
Azure Resource Graph Resource inventory queries (KQL)
Azure Monitor Metrics CPU, memory, connection utilisation
Azure Advisor Recommendation signals
Azure Cost Management Commitment coverage and spend data
Azure Subscription API Subscription name resolution

For CI/CD or automated scans, create a Service Principal with the Reader role:

az ad sp create-for-rbac \
  --name az-costguard \
  --role Reader \
  --scopes /subscriptions/<subscription-id>

Set the output values as environment variables AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET.


Installation

From PyPI (recommended)

pip install az-costguard

From source (development)

git clone https://github.com/ragnild/az-costguard.git
cd az-costguard

python -m venv .venv

# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate

pip install -e ".[dev]"

Docker

git clone https://github.com/ragnild/az-costguard.git
cd az-costguard
docker build -t az-costguard:latest .

Run a scan using a service principal:

docker run --rm \
  -e AZURE_TENANT_ID="<tenant-id>" \
  -e AZURE_CLIENT_ID="<client-id>" \
  -e AZURE_CLIENT_SECRET="<client-secret>" \
  az-costguard:latest scan -s <subscription-id>

Save findings to a file on your host:

docker run --rm \
  -e AZURE_TENANT_ID="<tenant-id>" \
  -e AZURE_CLIENT_ID="<client-id>" \
  -e AZURE_CLIENT_SECRET="<client-secret>" \
  -v "$PWD/output:/output" \
  az-costguard:latest scan -s <subscription-id> --format csv --output /output/findings.csv

Windows (PowerShell): replace \ line continuation with a backtick ` and $PWD with ${PWD}.


Authentication

Option 1 — Azure CLI (recommended for local use)

az login
az account set --subscription <subscription-id>

Option 2 — Service Principal

Set these environment variables before running:

AZURE_TENANT_ID=<tenant-id>
AZURE_CLIENT_ID=<client-id>
AZURE_CLIENT_SECRET=<client-secret>

az-costguard automatically picks up the service principal when all three variables are present.


Running a Scan

Scan all accessible subscriptions

az-costguard scan

Scan a specific subscription

az-costguard scan -s <subscription-id>

Scan multiple subscriptions

az-costguard scan -s <sub-id-1> -s <sub-id-2>

Verbose mode — see every rule result

az-costguard scan -s <subscription-id> --verbose

Shows a Rule Execution Detail table with the status of every evaluated rule (PASS / FOUND / ERROR / SKIPPED), resource names, locations, and estimated savings.


Output Formats

Table (default)

Prints a Category Summary and Overall Summary to the terminal.

az-costguard scan -s <subscription-id>

Table + Rule Execution Detail

az-costguard scan -s <subscription-id> --verbose

JSON

Outputs the findings array as JSON to stdout. Pipe or redirect as needed.

az-costguard scan -s <subscription-id> --format json

# Save to file
az-costguard scan -s <subscription-id> --format json > findings.json

CSV

Outputs findings as a CSV file with a header row.

az-costguard scan -s <subscription-id> --format csv

# Save to file
az-costguard scan -s <subscription-id> --format csv > findings.csv

CSV columns: rule_id, rule_name, domain, severity, resource_name, resource_id, resource_group, subscription_id, location, monthly_savings, annual_savings, confidence, risk, evidence, description, recommendation, suppressed

HTML Executive Report

Generate a self-contained interactive HTML report from a CSV export. Opens in any browser — no server required.

# Step 1 — export findings to CSV
az-costguard scan -s <subscription-id> --format csv > findings.csv

# Step 2 — generate HTML report
az-costguard report --csv findings.csv --output report.html

For multiple subscriptions, pass --csv once per file:

az-costguard report --csv sub1.csv --csv sub2.csv --output report.html

Filtering

By domain

az-costguard scan -s <subscription-id> --domain "Waste Elimination"

Available domains:

  • Waste Elimination
  • VM Rightsizing
  • Disk Optimization
  • Azure SQL
  • App Services & Functions
  • AKS
  • Networking
  • Storage Accounts
  • Data Services
  • Monitoring & Backup
  • Commitments
  • Governance & FinOps Intelligence

By minimum severity

az-costguard scan -s <subscription-id> --severity HIGH
az-costguard scan -s <subscription-id> --severity MEDIUM
az-costguard scan -s <subscription-id> --severity LOW

Combining filters

az-costguard scan -s <subscription-id> --domain "Networking" --severity MEDIUM --format csv > network-findings.csv

Command Reference

Usage: az-costguard [OPTIONS] COMMAND [ARGS]...

Options:
  -V, --version   Show version and exit.
  --help          Show this message and exit.

Commands:
  scan    Scan Azure subscriptions for cost optimization findings.
  report  Generate an interactive HTML executive report from CSV exports.

scan

Usage: az-costguard scan [OPTIONS]

Options:
  -s, --subscription TEXT   Subscription ID to scan. Repeatable.
                            Defaults to all accessible subscriptions.
  -d, --domain TEXT         Limit to a single domain.
  --severity TEXT           Minimum severity: HIGH, MEDIUM, or LOW.
  -f, --format TEXT         Output format: table, json, or csv.  [default: table]
  -c, --config TEXT         Path to az-costguard.yaml config file.
  -v, --verbose             Show rule execution detail for every evaluated rule.
  --help                    Show this message and exit.

report

Usage: az-costguard report [OPTIONS]

  Generate an interactive HTML executive report from one or more scan CSV files.

Options:
  --csv     PATH   CSV file produced by 'scan --format csv'. Repeatable.  [required]
  --output  PATH   Output HTML file path.  [default: az_costguard_report.html]
  --help           Show this message and exit.

Configuration File

Create az-costguard.yaml to set defaults or suppress known findings:

min_monthly_savings: 10.0   # Only report findings with >= $10/month savings

exceptions:
  - rule_id: azure.publicip.unused
    resource_id: /subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.Network/publicIPAddresses/<name>

Pass the config file with:

az-costguard scan -s <subscription-id> --config az-costguard.yaml

Example Output

Default mode

                      CATEGORY SUMMARY
 Category                    Evaluated  Triggered  Findings  Est. $/mo
 ──────────────────────────────────────────────────────────────────────
 Monitoring & Backup               3          3         4         —
 Networking                       11          2         8        $8
 Waste Elimination                12          2         2         —
 ──────────────────────────────────────────────────────────────────────
 Total                            26          7        14        $8

                      OVERALL SUMMARY
 ┌──────────────────────────────┬────────────────────────────────┐
 │ Subscriptions scanned        │ 1                              │
 │ Resources in scope           │ 4,823                          │
 │ Rules evaluated              │ 35                             │
 │ Total findings               │ 13                             │
 │ Estimated monthly savings    │ $7.30                          │
 ├──────────────────────────────┼────────────────────────────────┤
 │ Severity breakdown           │ HIGH: 0  MEDIUM: 13  LOW: 0    │
 ├──────────────────────────────┼────────────────────────────────┤
 │ Scan duration                │ 18.3s                          │
 └──────────────────────────────┴────────────────────────────────┘

Verbose mode — Rule Execution Detail (excerpt)

 Rule ID                           Status   Found  Risk    Est.$/mo  Resource(s)          Location    Reason
 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────
 azure.vm.stopped_not_deallocated   PASS         0  —           —     —                    —           —
 azure.vm.orphaned.nic              FOUND        6  MEDIUM      —     nic-001, nic-002 +4   uaenorth    Orphaned NIC not attached to any VM
 azure.publicip.unused              FOUND        2  MEDIUM     $8     pip-001, pip-002      uaenorth    PIP has no IP configuration. SKU: Standard
 azure.bastion.idle                 FOUND        1  MEDIUM      —     bastion-dev           uaenorth    Verify session activity
 azure.acr.unused                   SKIPPED      —  —           —     —                    —           No executor yet

 Rules evaluated  : 29
 Passed           : 24
 Triggered        :  5
 Errors           :  0
 Skipped          :  6

 Estimated savings from triggered rules : $7.30/month

Domains & Rule Count

Domain Rules
Waste Elimination 35
VM Rightsizing 27
Disk Optimization 14
Azure SQL 14
App Services & Functions 11
AKS 24
Networking 9
Storage Accounts 15
Data Services 12
Monitoring & Backup 11
Commitments 12
Governance & FinOps Intelligence 20
Total 204

Version

az-costguard --version

Metadata

Release files for az-costguard 0.1.0

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-costguard 0.1.0
File Size Uploaded Provenance
az_costguard-0.1.0.tar.gz 124.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for az-costguard 0.1.0
File Interpreter ABI Platform
az_costguard-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 264.5 kB

Release files / az_costguard-0.1.0.tar.gz

Download URL az_costguard-0.1.0.tar.gz
Size 124.2 kB
Tags Source
SHA-256 checksum
How to use checksums
a1b9f86b2d3595ed053fd1fe782245e59d47d50b6cdfb36dac49b39bc516825c
BLAKE2b-256 checksum
How to use checksums
e01645bb2af76cf9bbf9ef016d65869d7af0ff7161e355c49e1a17e00a30669f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / az_costguard-0.1.0-py3-none-any.whl

Download URL az_costguard-0.1.0-py3-none-any.whl
Size 140.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a3d0c4ef3a44da2eab1c71c54da6f4b3b02275c47832eda1517485a7f584844b
BLAKE2b-256 checksum
How to use checksums
3898aaa7c86ff3c25244055fb2b7678288f180da902ad4cf82a89fba9218ddf7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Security for az-costguard 0.1.0

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.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

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