Well-Architected Framework: Cost Optimisation

The Microsoft Well-Architected Framework (WAF) gives us five key pillars to ensure our workloads are designed and operated in a way that’s optimised for the cloud. One of those pillars is Cost Optimisation, and yet it’s consistently one of the most neglected areas, pushed to the side until someone receives a rather large unexpected bill.

Over the coming weeks I’m going to cover all five pillars and how they apply to real Azure Platform Design decisions:

This week, we start with Cost Optimisation.

What Do We Mean by Cost Optimisation?

The reality of the cloud is that everything has a cost. Take a virtual machine as a simple example. There are costs for compute (CPU and memory), managed disks, networking egress, backups, and licensing. When you think about it carefully, those same costs existed in a traditional on-premises data centre: server hardware, racking infrastructure, HVAC, electricity, networking components, and connectivity links. The difference is largely in how we pay.

In the cloud, we get a monthly bill based on usage. In a traditional data centre, we typically pay upfront for hardware and incur ongoing energy costs. The billing model is different, but the fundamental principle is the same – and the cloud gives us one significant advantage: if we don’t use it, we don’t pay for it.

That’s the key to cost optimisation. It’s not about reducing costs for the sake of it. It’s about making sure that what we’re paying for is genuinely what’s required – no more, no less.

Design Cost In From the Start

Cost decisions made late are expensive to reverse. I’ve written about bringing cost awareness to the forefront of the delivery process in my post Shift Cost Left: Adding Infracost to Your IaC Pipelines, which shows how to surface the cost impact of infrastructure changes directly within your DevOps pull requests, before anything is deployed.

But tooling aside, the principle is simple: cost must be considered at the very start of any design. Understand what the deployment will look like, which SKU sizes actually meet the requirements, and who is funding the work. Ensure budget approval is in place before resources are deployed. These steps feel obvious, but they’re consistently skipped, and they’re what prevents that difficult conversation when the unexpected bill arrives.

Understand and Model Your Usage Patterns

One of the genuine advantages of the cloud over traditional infrastructure is how straightforward it is to scale up or out with minimal disruption. That flexibility changes how we should think about initial sizing, don’t over-provision just in case. Right-size from the start, knowing that scaling up later is simple.

My usual approach is to start slightly smaller than what we think we need. Scaling up is always perceived as less disruptive than scaling back down, and it gives you real usage data to inform those decisions early.

We have good tooling available to support this. Azure Advisor will flag under- and over-provisioned resources, and Azure Monitor Metrics give us the data to validate that systems are performing as designed.

What works on day one won’t necessarily work on day one hundred. Get into the habit of monitoring your solutions, understanding your baselines, and learning to anticipate when increased capacity will be needed, then scale up and back down accordingly. Where the solution allows it, use autoscaling to handle this dynamically rather than manually. Azure Monitor Application Insights can provide useful insight into application usage patterns to inform those decisions.

Use the Right Pricing Models

Microsoft offers several ways to reduce costs through commitment. Reserved Instances give around a 30% discount in exchange for committing to a specific resource SKU and region for one to three years. Savings Plans work differently – you commit to a specific hourly spend for a period of time, rather than a specific resource sku, giving more flexibility if your workloads are dynamic or you can’t commit to a fixed SKU.

The rule of thumb is straightforward: if you know the SKU and region, and they’re likely to remain stable, a Reserved Instance is the better option. If your resources are more fluid, a Savings Plan is the more sensible choice. Microsoft’s own guide on choosing between the two is worth reading if you’re evaluating both options.

Beyond reservations and savings plans, there are other pricing levers worth knowing. Spot Instances for virtual machines can dramatically reduce running costs. They’re well suited to dev and test environments where guaranteed availability isn’t a hard requirement.

Monitor and Govern Spend

Cost optimisation is never a one-time activity. Set and forget doesn’t work here.

Azure Cost Management gives us budgets and budget alerts, and every deployment should have them. These can be created automatically as part of your Terraform CI/CD pipelines, removing the manual step entirely.

One thing I consistently see teams get wrong is who the alerts go to. Teams often send budget notifications to a distribution group, and the problem with that is nobody owns it. When an alert fires and lands in a shared inbox, it’s easy for everyone to assume someone else is dealing with it. Designate one or two named budget owners per solution. They receive the alert, they own the response. It removes the “I thought you were handling that” conversation entirely.

Budgets also need to be treated as living documents, not a one-time configuration. A budget set at the start of a project may be completely irrelevant six months later. When a budget is consistently exceeded, or consistently never reached, it should be reviewed and adjusted. I covered a practical example of this in my Landing Zone design post using Log Analytics ingestion alerts: when an alert fires, the owner determines whether it’s a genuine problem or a sign that usage patterns have legitimately changed, and adjusts the threshold accordingly. The same principle applies to budgets.

Setting multiple thresholds makes sense, something around 70%, 90%, and 100% of budget gives early warning without generating so many alerts that people start ignoring them.

Underpinning all of this, I find the most effective approach is a regular cadence with business owners and stakeholders where we review deployed resources and their associated costs. A quarterly service review gives you a structured opportunity to address concerns early and make changes before they become problems. It’s surprising how much can be saved through a straightforward conversation.

Accountability Through Tagging and Chargeback

Without clear ownership of costs, nobody takes accountability for them. Tagging is the foundation that makes cost attribution possible.

Resource tags can be applied at the subscription, resource group, or individual resource level. Defining a consistent set of billing-related tags: cost centre, environment, owner, workload, allows you to allocate costs back to the relevant parts of the business and have a meaningful conversation about them.

Tags are only useful when they’re standardised, and Azure doesn’t enforce that by default. Anyone can create tags with whatever values they like, which quickly leads to inconsistent data and reports you can’t trust. Azure Policy is the answer here; it can restrict tags to approved values, automatically inherit tags from resource groups and subscriptions, and prevent resources from being created without required tags. A key callout with tags, is that if you update a billing tag, you will lose the historical data in your billing reports for the previous tag value. For this reason alone, getting your tag taxonomy defined and your policies in place before you start deploying resources is key. Retrofitting tags across an existing environment is a painful exercise.

Use Restrictions With Care

We want to enable teams to move quickly, but within boundaries that prevent costly mistakes. Azure Policy lets us restrict which resource SKUs can be deployed, for example, limiting virtual machine sizes to an approved list, or blocking specific premium tiers in non-production environments. Used alongside a well-designed RBAC model, this gives you real governance without blocking legitimate work.

One word of caution though: Azure SKUs change frequently. New sizes are released, old ones are retired, and pricing tiers shift. Any policy that restricts resource types needs to be maintained actively, a restriction that made sense twelve months ago may now be blocking a more cost-effective option than the one it was designed to protect.

Stop and Deallocate Idle Resources

We talked about scaling resources up and down dynamically, and virtual machines and Virtual Machine Scale Sets support this particularly well. Azure Virtual Desktop environments, for example, can have a scaling policy applied that automatically adjusts capacity as demand changes throughout the day, Scale Sets work on the same principle.

For other workloads that don’t support dynamic scaling, we have another option: scheduled start and stop. Certain resource types, including some SQL SKUs, Fabric Capacities, and standalone virtual machines, can be configured to run only during defined hours. A dev environment that runs 9 to 5 on weekdays costs a fraction of one running around the clock.

It’s worth being clear on what “stopped” means for billing though. When a virtual machine is deallocated, you stop paying for compute, but you continue to pay for the managed disk. It’s not zero cost, but for intermittent workloads, it’s a significant saving, and it removes the need to constantly redeploy resources that are only needed periodically.

Summary

Cost optimisation isn’t the most exciting area of cloud engineering, but it’s one of the most important. Done well, it’s largely invisible: budgets that rarely fire because spending is well understood, resources that are right-sized from the start, and tags that make attribution straightforward. Done badly, it’s the conversation nobody wants to have when the bill arrives.

Many organisations now treat cost optimisation as part of a broader FinOps practice, where engineering, finance, and business stakeholders collaborate to manage cloud spend effectively. The technical controls discussed here form the foundation for that model.

The crucial step most teams skip is spending the time upfront, before resources are deployed, to think about cost as a design concern rather than an operational one. Build it into your processes, automate what you can, and review regularly. It’s not glamorous, but it protects both the platform and the business.

Your subscription could not be saved. Please try again.
Your subscription to deniscooper.co.uk has been successful.

Join Cloud Engineers Who Build Smarter on Azure

Free checklists, practical guides and the latest posts — built for cloud engineers working with Azure and Terraform.

Leave a Comment