Skip to main content

Azure cost optimization scanner — 204 rules across 12 domains

Project description

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

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_costguard-0.1.0.tar.gz (124.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_costguard-0.1.0-py3-none-any.whl (140.2 kB view details)

Uploaded Python 3

File details

Details for the file az_costguard-0.1.0.tar.gz.

File metadata

  • Download URL: az_costguard-0.1.0.tar.gz
  • Upload date:
  • Size: 124.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for az_costguard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 a1b9f86b2d3595ed053fd1fe782245e59d47d50b6cdfb36dac49b39bc516825c
MD5 f38eb61c9984975f44ff83fb0b87c8d3
BLAKE2b-256 e01645bb2af76cf9bbf9ef016d65869d7af0ff7161e355c49e1a17e00a30669f

See more details on using hashes here.

File details

Details for the file az_costguard-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: az_costguard-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 140.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for az_costguard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a3d0c4ef3a44da2eab1c71c54da6f4b3b02275c47832eda1517485a7f584844b
MD5 a724956c06142d800f9c7fc8457230a3
BLAKE2b-256 3898aaa7c86ff3c25244055fb2b7678288f180da902ad4cf82a89fba9218ddf7

See more details on using hashes here.

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