FinOps & Governance · Jan 2026 · 10 min read
FinOps in Practice: Stop Wasting Money on Idle Azure Resources
A practical guide to using Azure Resource Graph, Azure Policy, Azure Advisor, Cost Management, and automation to identify unused resources, reduce cloud waste, and establish sustainable FinOps governance.
Introduction: Where Azure Cost Waste Hides
Cloud waste rarely comes from one obviously expensive resource. It often accumulates from dozens or hundreds of small decisions:
- Disks left behind after VM deletion
- Unused public IP addresses
- Stopped or underutilized VMs
- Oversized Azure SQL databases
- Old snapshots
- Unused storage
- Overprovisioned Kubernetes nodes
- Excessive log retention
- Non-production resources running 24/7
- Resources without owners or cost-center tags
The goal of FinOps is not simply to "spend less."
The goal is to ensure that every cloud resource has a business purpose and that the organization gets measurable value from its cloud investment.
A good FinOps workflow looks like:
Discover
↓
Measure
↓
Analyze
↓
Optimize
↓
Automate
↓
Monitor
1. Finding Orphaned Managed Disks
One of the easiest places to begin is with unattached Azure Managed Disks.
When a VM is deleted, its disks may remain depending on the deletion configuration and lifecycle policies.
These disks can continue generating storage charges even though they are no longer serving an active workload.
Azure Resource Graph Query
Run the following KQL query in Azure Resource Graph Explorer:
Resources
| where type =~ "microsoft.compute/disks"
| extend diskState = tostring(properties.diskState)
| where diskState =~ "Unattached"
| project
name,
resourceGroup,
subscriptionId,
location,
diskState,
sku = tostring(sku.name),
diskSizeGB = toint(properties.diskSizeGB),
resourceId = id
| order by diskSizeGB desc
This gives you a useful inventory of potentially orphaned disks.
Important
Do not automatically delete every unattached disk.
An unattached disk may still be:
- A backup disk
- A disaster-recovery resource
- A migration artifact
- A staging resource
- A manually managed data disk
- Required for an upcoming deployment
Use an approval workflow before deletion.
A safer process is:
Unattached Disk
↓
Identify Owner
↓
Check Tags
↓
Check Activity / Age
↓
Confirm Business Purpose
↓
Approval
↓
Delete
2. Finding Unattached Public IP Addresses
Public IP addresses can also become orphaned when resources are deleted.
Use Azure Resource Graph to identify Public IP resources that do not have an active IP configuration.
Resources
| where type =~ "microsoft.network/publicipaddresses"
| extend ipConfig = tostring(properties.ipConfiguration.id)
| where isempty(ipConfig)
| project
name,
resourceGroup,
subscriptionId,
location,
sku = tostring(sku.name),
resourceId = id
| order by name asc
Again, treat the result as a candidate cleanup list, not an automatic deletion list.
A public IP may have a legitimate purpose even if it is temporarily unattached.
3. Finding Unattached Network Interfaces
Network interfaces can also remain after VM deletion.
A useful Resource Graph query is:
Resources
| where type =~ "microsoft.network/networkinterfaces"
| extend vmId = tostring(properties.virtualMachine.id)
| where isempty(vmId)
| project
name,
resourceGroup,
subscriptionId,
location,
resourceId = id
| order by name asc
This helps identify NICs that are no longer associated with a VM.
Before deleting them, check whether they are being referenced by another architecture component or reserved for future deployment.
4. Find Old Snapshots
Snapshots can accumulate over time, particularly in environments with frequent testing, migration, or backup activities.
Use:
Resources
| where type =~ "microsoft.compute/snapshots"
| project
name,
resourceGroup,
subscriptionId,
location,
diskSizeGB = toint(properties.diskSizeGB),
timeCreated = todatetime(properties.timeCreated),
resourceId = id
| order by timeCreated asc
You can then identify snapshots that exceed your organization's retention period.
For example:
Snapshot age > 90 days
↓
Check retention policy
↓
Check owner
↓
Check backup requirements
↓
Delete if approved
5. Identify Underutilized Virtual Machines
Deleting unused resources is only one part of FinOps.
A potentially larger saving opportunity is right-sizing active resources.
For VMs, examine metrics such as:
- CPU utilization
- Memory utilization
- Network throughput
- Disk I/O
- Application performance
- Peak utilization
For example:
VM Size: Standard_D8s_v5
Average CPU: 8%
Peak CPU: 22%
If the workload consistently operates far below its allocated capacity, investigate whether the VM can be downsized.
However, do not right-size based solely on average CPU.
Consider:
- Peak traffic
- Memory requirements
- Application latency
- Disk performance
- Network requirements
- Business-critical workloads
- Seasonal demand
A better approach is:
Measure → Analyze → Test → Right-size → Monitor
6. Azure SQL Right-Sizing
Azure SQL is another important FinOps optimization area.
Organizations frequently provision more compute than the workload actually requires.
Review:
- CPU utilization
- Data I/O
- Log I/O
- Storage utilization
- Query performance
- DTU/vCore utilization
- Connection counts
- Workload patterns
Analyze at least several weeks of production data where possible, rather than making decisions from a single day's metrics.
7. Azure SQL Serverless
For workloads with intermittent or unpredictable usage, Azure SQL Database serverless compute can be an option.
Serverless can dynamically scale compute based on workload and can automatically pause compute during periods of inactivity when configured and supported by the workload.
This can be particularly useful for:
- Development environments
- Test environments
- Low-volume applications
- Intermittent workloads
However, serverless is not automatically cheaper for every database.
Evaluate:
- Workload pattern
- Auto-pause requirements
- Resume latency
- Minimum compute
- Storage requirements
- Application behaviour
FinOps means choosing the right pricing model for the workload, not simply choosing the feature with the lowest advertised price.
8. Azure SQL Elastic Pools
If an organization operates many Azure SQL databases with inconsistent utilization, Elastic Pools can allow databases to share a pool of compute resources.
For example:
Before:
Database A → Dedicated Compute
Database B → Dedicated Compute
Database C → Dedicated Compute
Database D → Dedicated Compute
Potential alternative:
After:
Azure SQL Elastic Pool
+------------------------+
| |
| DB A DB B DB C DB D |
| |
+------------------------+
This can improve resource utilization when database workloads have different usage patterns.
Always validate workload characteristics before migrating.
9. Azure Advisor and Cost Management
Do not rely exclusively on custom Resource Graph queries.
Use Azure's native FinOps capabilities as well.
Important services include:
Azure Cost Management
Use it to analyze:
- Actual spend
- Forecasted spend
- Cost by subscription
- Cost by resource group
- Cost by service
- Cost by tag
- Cost trends
Azure Advisor
Use recommendations related to:
- Right-sizing
- Idle resources
- Reserved capacity
- Savings opportunities
- Reliability
- Security
The combination provides:
Azure Cost Management
+
Azure Advisor
+
Azure Resource Graph
+
Azure Monitor
↓
FinOps Visibility
10. Enforce Resource Tagging
FinOps becomes significantly harder when resources do not have clear ownership.
Establish a mandatory tagging strategy.
For example:
Environment = Production
Owner = Platform-Team
CostCenter = Engineering
Application = Customer-API
Project = SaaS-Platform
ManagedBy = Terraform
Use Azure Policy to audit or enforce required tags.
For example:
Resource Created
↓
Azure Policy
↓
Required Tags?
/ YES NO
| |
Allow Deny/Audit
Be careful with strict Deny policies in production. Introduce tagging policies gradually and validate their impact before enforcement.
11. Automate Dev/Test Shutdown
Non-production environments are often left running overnight.
For example:
Developer VM
|
+---- Monday–Friday
| 08:00–19:00
|
+---- Weekend
OFF
For eligible workloads, implement automated schedules using Azure-native automation capabilities.
Possible approaches include:
- Azure Automation
- Azure Functions
- Logic Apps
- VM auto-shutdown capabilities
- Scheduled automation workflows
The exact implementation should account for VM dependencies and development workflows.
12. Detect Resources Without Owners
A powerful FinOps governance strategy is to identify resources that cannot be attributed to a team or application.
Example Resource Graph query:
Resources
| extend owner = tostring(tags["Owner"])
| extend environment = tostring(tags["Environment"])
| extend costCenter = tostring(tags["CostCenter"])
| where isempty(owner)
or isempty(environment)
or isempty(costCenter)
| project
name,
type,
resourceGroup,
subscriptionId,
location,
owner,
environment,
costCenter,
resourceId = id
This creates a governance backlog.
Instead of immediately deleting these resources:
Missing Metadata
↓
Identify Owner
↓
Assign Cost Center
↓
Classify Workload
↓
Optimize
13. Build an Automated FinOps Workflow
A mature FinOps process should move beyond manually running queries.
A practical workflow is:
Azure Resources
|
v
Azure Resource Graph
|
v
Identify Candidates
|
v
Azure Monitor / Cost Management
|
v
Validate Utilization
|
v
FinOps Rules
|
v
Approval
|
+--------+
| |
v v
Optimize Delete
| |
+--------+
|
v
Cost Reduction
|
v
Reporting
Automation can be implemented using:
- Azure Functions
- Azure Automation
- Logic Apps
- GitHub Actions
- Azure DevOps
- PowerShell
- Python
For destructive actions, use an approval or quarantine stage rather than immediately deleting resources.
14. Create a Resource Quarantine Strategy
Instead of:
Detect → Delete
use:
Detect
↓
Tag as Candidate
↓
Notify Owner
↓
Grace Period
↓
Final Validation
↓
Approval
↓
Delete
For example, a candidate resource could receive:
FinOpsStatus = CleanupCandidate
CleanupAfter = 2026-04-01
This gives owners an opportunity to recover resources that were incorrectly identified.
15. Measure FinOps Success
FinOps should produce measurable business outcomes.
Track metrics such as:
Cloud Waste
Potential Waste / Total Cloud Spend
Optimization Savings
Baseline Cost - Optimized Cost
Resource Ownership
Tagged Resources / Total Resources × 100
Idle Resource Reduction
Idle Resources Before
↓
Idle Resources After
Also measure:
- Monthly cloud spend
- Cost per application
- Cost per customer
- Cost per transaction
- Cost per environment
- Forecast accuracy
- Optimization savings
- Commitment utilization
Operational Checklist
Before implementing automated cleanup:
- Enable Azure Cost Management reporting.
- Review Azure Advisor recommendations.
- Inventory resources with Azure Resource Graph.
- Identify unattached managed disks.
- Identify unattached NICs.
- Identify unattached public IPs.
- Review old snapshots.
- Identify underutilized VMs.
- Review Azure SQL utilization.
- Evaluate serverless workloads.
- Evaluate SQL Elastic Pools.
- Establish mandatory tagging.
- Configure Azure Policy.
- Implement dev/test shutdown schedules.
- Establish resource ownership.
- Create a cleanup-candidate workflow.
- Require approval before destructive actions.
- Monitor savings after optimization.
- Report FinOps KPIs to stakeholders.
Final Takeaway
FinOps is not a one-time cleanup exercise.
It is a continuous engineering discipline:
Discover → Measure → Optimize → Govern → Automate → Monitor
The objective is not to make Azure as cheap as possible.
The objective is to make Azure efficient, predictable, accountable, and aligned with business value.
A mature Azure FinOps practice combines:
Azure Cost Management + Azure Advisor + Azure Resource Graph + Azure Monitor + Azure Policy + Automation
That combination turns cloud cost management from a monthly finance exercise into an automated engineering practice.
FinOps · Azure Cost Optimization · Resource Graph · Azure Policy · Governance · Cloud Economics