DEV Community

Cover image for FinOps in Practice: Stop Wasting Money on Idle Azure Resources
Raphael Gab-Momoh
Raphael Gab-Momoh

Posted on Originally published at raphaelgmomoh.pages.dev

FinOps in Practice: Stop Wasting Money on Idle Azure Resources

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.

Azure Resource Graph overview

A good FinOps workflow looks like:

Discover
   ↓
Measure
   ↓
Analyze
   ↓
Optimize
   ↓
Automate
   ↓
Monitor
Enter fullscreen mode Exit fullscreen mode

Before the how, the what — three tools this guide relies on:

  • Azure Resource Graph — a query service that lets you search across every resource in a subscription (or many subscriptions) at once, using a SQL-like query language (KQL). Where the Azure Portal shows you resources one blade at a time, Resource Graph answers questions like "show me every unattached disk across the whole tenant" in a single query.
  • Azure Policy — a service that enforces rules about what's allowed to exist in a subscription, either by blocking non-compliant resources at creation or by continuously auditing existing ones against a rule. It's the mechanism that turns "we should tag everything" from a wish into something actually checked automatically.
  • Azure Advisor — a built-in recommendation engine that analyzes your actual usage patterns and suggests specific actions (resize this VM, delete that idle resource) based on Microsoft's own telemetry and best-practice rules, rather than requiring you to write the analysis query yourself.

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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%
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Potential alternative:

After:

          Azure SQL Elastic Pool
        +------------------------+
        |                        |
        | DB A  DB B  DB C  DB D |
        |                        |
        +------------------------+
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Use Azure Policy to audit or enforce required tags.

For example:

Resource Created
       ↓
Azure Policy
       ↓
Required Tags?
    /         YES        NO
   |          |
 Allow     Deny/Audit
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

This creates a governance backlog.

Instead of immediately deleting these resources:

Missing Metadata
      ↓
Identify Owner
      ↓
Assign Cost Center
      ↓
Classify Workload
      ↓
Optimize
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

use:

Detect
   ↓
Tag as Candidate
   ↓
Notify Owner
   ↓
Grace Period
   ↓
Final Validation
   ↓
Approval
   ↓
Delete
Enter fullscreen mode Exit fullscreen mode

For example, a candidate resource could receive:

FinOpsStatus = CleanupCandidate
CleanupAfter = 2026-04-01
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Optimization Savings

Baseline Cost - Optimized Cost
Enter fullscreen mode Exit fullscreen mode

Resource Ownership

Tagged Resources / Total Resources × 100
Enter fullscreen mode Exit fullscreen mode

Idle Resource Reduction

Idle Resources Before
        ↓
Idle Resources After
Enter fullscreen mode Exit fullscreen mode

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


Originally published on my portfolio.

Top comments (0)