← Back to Projects

FinOps: Turning Off What Nobody Uses

An estate-wide Azure cost review where every finding was validated against live state before it reached leadership

Platform: Azure (all subscriptions)
Tools: Cost Management + Resource Graph
Output: Prioritised savings report
Role: Analyst & author

Overview

Cost dashboards were full of "potential savings", but many of them were stale or wrong. Resources were already compliant, already stopped or still needed. I ran an estate-wide review and only reported what I'd checked against live resource state. The result was a prioritised plan worth a five-figure sum every month, including quick wins that could be actioned straight away with no risk.

Findings

PriorityFindingShare of savingsRisk
1Non-production compute running 24/7: more than a dozen AKS clusters plus several VMsLargest (more than half)Low, with opt-out tags
2Idle managed workflow schedulers in dev and testLargeLow
3Licence benefit not applied to eligible production VMsSmallNone (quick win)
4Orphaned storage: a multi-terabyte snapshot set and an unattached premium diskSmallConfirm with the owner first

Method

  1. Inventory everything with Resource Graph: power state, SKU, attachment state, tags and shutdown schedules.
  2. Validate each candidate. Several VMs already had shutdown schedules, and one GPU VM was already deallocated, so I excluded them. One VM had a schedule that was disabled.
  3. Trace orphans to their cause. The snapshots came from a past database migration, so the recommendation was "confirm with that change's owner", not "delete on sight".
  4. Flag compliance as well as cost. A developer-edition licence turned up in production during the licensing review.
unattached disks (Resource Graph)
resources
| where type =~ 'microsoft.compute/disks'
| where properties.diskState =~ 'Unattached'
| project name, resourceGroup, sku = sku.name,
          sizeGB = toint(properties.diskSizeGB), created = properties.timeCreated
| order by sizeGB desc

The AKS Lever

AKS clusters can't stop themselves, and an in-cluster CronJob can't start the cluster it stopped. So I recommended an external schedule, with opt-in by tag so teams stay in control:

Scheduled pipelineweekdays 20:00Find clusterstag autoshutdown=trueaz aks stopWeekdays 07:00az aks start
Out-of-hours schedule for non-production clusters
stop schedule (illustrative)
schedules:
  - cron: "0 20 * * 1-5"
    branches: { include: [ main ] }
    always: true
steps:
  - task: AzureCLI@2
    inputs:
      azureSubscription: nonprod-automation
      scriptType: bash
      inlineScript: |
        az aks list --query "[?tags.autoshutdown=='true'].[name,resourceGroup]" -o tsv |
        while read -r name rg; do az aks stop -n "$name" -g "$rg" --no-wait; done

Key Outcomes

Lessons Learned

Conclusion

The biggest saving wasn't exotic. It was switching things off when nobody uses them. The value was in the evidence behind each line.

Technologies Used

Azure Cost ManagementAzure Resource GraphKQLAzure CLIAzure AdvisorAKS Start/StopAuto-shutdownAzure Hybrid BenefitBudgets

About this case study: this write-up is drawn from my hands-on platform engineering experience. Organisation names, system names, figures and timelines have been changed or generalised, and all code, configuration and queries are illustrative examples written for this page.

← Hub-and-Spoke Landing Zone with Authoritative Private DNSKeyless CI/CD with OIDC for Terraform →
← Back to Projects Get in Touch