Appearance
Cost Management Guide
Overview
How to monitor and investigate Azure and AWS spend, understand what drives costs, respond to budget alerts, and keep cloud costs under control. ops-automation runs weekly cost reports — this guide explains how to read them and what to do when something spikes.
Azure Cost Monitoring
Azure Cost Analysis (Portal)
Azure Portal → Cost Management + Billing → Cost analysis
Switch the subscription scope at the top to see costs per subscription. Key views:
| View | How to Use |
|---|---|
| Accumulated cost | Month-to-date vs budget. Switch to daily granularity to see which day costs spiked. |
| Cost by resource | Sort by cost descending to find the top spenders in a subscription |
| Cost by service | Which Azure service category is driving cost (Compute, Storage, Networking, Monitor) |
Filter by subscription — check each of the six PROD subscriptions separately: Production, Logging, Identity, Firewall, Development, Main.
The Logging subscription is often the highest due to Log Analytics ingestion. The Production subscription has VM compute costs that vary with VM uptime.
Log Analytics Ingestion Cost
The biggest Azure cost lever. Log Analytics charges per GB ingested.
bash
# Check current month ingestion by table
az monitor log-analytics query --workspace 5d76d1f2 \
--analytics-query "Usage | where TimeGenerated > startofmonth(now()) | where IsBillable == true | summarize GB = sum(Quantity)/1000 by DataType | sort by GB desc" \
--output tableIf ingestion is spiking, find the table growing fastest and trace it to the agent or diagnostic setting sending the data. Common culprits:
CommonSecurityLog— firewall logs; reduce Palo Alto log verbosity if neededSecurityEvent— Windows event logs; check if a DC started logging excessivelyContainerLog— Container Apps; check for a noisy agent crashing in a loop
Hard cap on LAW ingestion is never set — inappropriate in a HIPAA environment. Use threshold alerts instead (see Budget Alerts below).
Azure Budgets and Alerts
Each subscription has a budget configured in Azure Cost Management. Alerts fire at 80% and 100% of the monthly budget.
To view or update budgets: Azure Portal → Cost Management + Billing → Budgets (switch subscription scope)
Budget alert emails go to the IT admin email. If an alert fires:
- Go to Cost Analysis for the subscription
- Find which resource or service caused the spike
- Determine if it's expected (new deployment, DR test) or unexpected (runaway process)
Common Azure Cost Spikes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Compute spike mid-month | VM left running that should be stopped | Deallocate the VM |
| Storage spike | Log archive growing, large blob upload | Check archive storage; adjust retention if over-retaining |
| Networking spike | Data transfer out of Azure (egress) | Check what's sending large volumes outbound |
| Monitor spike | Diagnostic settings sending too much to LAW | Reduce log verbosity or filter at the diagnostic setting |
| Container Apps spike | Revision consuming too many replicas | Check Container App scaling rules |
AWS Cost Monitoring
AWS Cost Explorer
bash
# Current month spend by service across all accounts
aws ce get-cost-and-usage \
--profile main \
--time-period Start=$(date +%Y-%m-01),End=$(date +%Y-%m-%d) \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=DIMENSION,Key=SERVICE \
--output tableOr in the AWS console: Management account → Cost Explorer → by service.
The Management account (206820231356) has consolidated billing — all org account costs roll up here. Use Cost Explorer from the management account to see the full picture.
Per-Account Cost Breakdown
bash
# Spend by linked account this month
aws ce get-cost-and-usage \
--profile main \
--time-period Start=$(date +%Y-%m-01),End=$(date +%Y-%m-%d) \
--granularity MONTHLY \
--metrics BlendedCost \
--group-by Type=DIMENSION,Key=LINKED_ACCOUNT \
--output tableMost AWS cost is in the Production account (807267566999) (EC2 DR instances) and Backup account (863609217450) (S3 + Glacier storage for Veeam).
AWS Budgets
Budget alerts are configured per account in the management account: AWS Console → Management account → Billing → Budgets
Alerts fire at 80% and 100% thresholds and go to IT admin email.
Common AWS Cost Spikes
| Symptom | Likely Cause | Fix |
|---|---|---|
| EC2 spike | DR instances left running after a test | Stop instances in Production account |
| S3/Glacier spike | Veeam backup growth | Check backup retention policy in Veeam |
| Data transfer spike | Large restore from Glacier or cross-region transfer | Check Veeam restore jobs; Glacier Expedited retrieval is expensive |
| NAT Gateway spike | High outbound traffic from DR VPC | Check what's generating traffic in the DR environment |
Glacier Retrieval Costs
Retrieving from S3 Glacier has tiered pricing:
- Expedited (1–5 min): ~$0.03/GB + $0.01/request — use only in real DR
- Standard (3–5h): ~$0.01/GB — use for planned restores and testing
- Bulk (5–12h): ~$0.0025/GB — use for large archive exports
Always use Standard or Bulk for non-urgent restores. Expedited during a real DR event is acceptable.
ops-automation Cost Reports
ops-automation runs weekly cost reports that email a summary to IT admin. When the report arrives:
- Check the week-over-week delta — a >20% increase without a known cause is worth investigating
- The email breaks down top 5 cost drivers per cloud
- If something looks wrong, use Cost Explorer / Cost Analysis to drill in
If the cost report email stops arriving: check ops-automation GitHub Actions for the weekly-cost-report.yml workflow. A failing run means the report isn't generating.
Cost Optimization Opportunities
Items worth reviewing quarterly:
| Item | How to Check | Action |
|---|---|---|
| Stopped (not deallocated) Azure VMs | Azure Portal → VMs → Power state | Deallocate stopped VMs — stopped VMs still charge for compute |
| Unused public IPs | Azure → Public IP addresses → dissociated | Delete unattached public IPs |
| Old Azure snapshots | Azure → Snapshots → sort by date | Delete snapshots >90 days unless needed for compliance |
| S3 buckets missing lifecycle rules | aws s3api get-bucket-lifecycle-configuration --bucket <name> | Add lifecycle rules to transition old objects to Glacier |
| Orphaned EBS volumes | aws ec2 describe-volumes --filters Name=status,Values=available | Delete unattached volumes |
| Log Analytics over-retention | LAW → Usage and estimated costs → Data retention | Cirius standard is 90 days hot + 6-year archive via export; don't pay for hot retention beyond 90 days |
Related Documents
- AWS Account Access Guide — authenticating to run cost CLI commands
- Azure Daily Operations — Azure portal and CLI basics
- Backup Architecture — S3/Glacier storage details
Document History
| Date | Change | Author |
|---|---|---|
| May 2026 | Initial draft — Azure Cost Analysis, LAW ingestion monitoring, AWS Cost Explorer, budget alerts, common spikes, ops-automation reports, optimization checklist. | Rory |