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$PWDwith${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 EliminationVM RightsizingDisk OptimizationAzure SQLApp Services & FunctionsAKSNetworkingStorage AccountsData ServicesMonitoring & BackupCommitmentsGovernance & 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a1b9f86b2d3595ed053fd1fe782245e59d47d50b6cdfb36dac49b39bc516825c
|
|
| MD5 |
f38eb61c9984975f44ff83fb0b87c8d3
|
|
| BLAKE2b-256 |
e01645bb2af76cf9bbf9ef016d65869d7af0ff7161e355c49e1a17e00a30669f
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a3d0c4ef3a44da2eab1c71c54da6f4b3b02275c47832eda1517485a7f584844b
|
|
| MD5 |
a724956c06142d800f9c7fc8457230a3
|
|
| BLAKE2b-256 |
3898aaa7c86ff3c25244055fb2b7678288f180da902ad4cf82a89fba9218ddf7
|