[SQL 4] SQL Server on an Azure VM: Full Control, Full Bill

The infrastructure option, argued honestly. Why the SLA never covers SQL Server, what the IaaS Agent extension is actually for, how the license can cost more than the machine it runs on, and the workloads that genuinely belong here.


Running SQL Server on an Azure virtual machine is the option people apologise for choosing, and they should stop. It is the actual product, with every feature it has ever had, on hardware you no longer have to own. What it is not is a managed service, and the gap between those two things is wider than the marketing suggests: the availability commitment does not cover the database engine, the patching is still yours, and on a small machine the software license can cost more than the machine. Choose it deliberately, for reasons you can name, and it is an excellent answer. Choose it by default because it felt safe and you have simply moved your problems into someone else’s building.


The service level agreement does not cover SQL Server

Start here, because it is the single most misunderstood fact about this option and Microsoft states it plainly enough that there is no excuse for the confusion. The Azure service level agreement covers the virtual machine. It does not cover the processes running on the virtual machine, and the documentation names SQL Server specifically as an example. It goes further, and this is the part that surprises people: building a supported high availability configuration does not earn you an additional commitment. An availability group across zones does not come with a database service level agreement. It comes with a better-protected pair of virtual machines and an architecture you are responsible for.

The infrastructure numbers themselves are worth knowing in outline: a single virtual machine on premium storage carries the lowest commitment, an availability set improves it, and two or more machines spread across two or more availability zones carries the highest. Those are commitments about the machines being reachable, nothing more. Everything above that line, the engine being healthy, the failover actually completing, the listener resolving to the right node, is yours to design, test, and answer for.

On a platform service you buy an outcome. On a virtual machine you rent the conditions in which you might achieve one. Both are legitimate purchases. Only one of them is a guarantee.

I labour this because of how these designs get written. A document says “SQL Server on Azure VMs with an availability group across availability zones, 99.99%”, and everyone reads it as a promise. It is not a promise, it is an aspiration resting on the customer’s own engineering, and the difference becomes visible during the first real failover when someone asks who is accountable. Write it accurately and the operational conversation that follows is a much better one.

The extension is not optional in practice

The SQL IaaS Agent extension is the thing that makes a SQL Server virtual machine an Azure-managed SQL Server virtual machine rather than a machine that happens to have SQL Server on it. It is what surfaces the instance in the portal as a manageable resource, and it delivers automated backup, patch orchestration, Key Vault integration, tempdb configuration, the best practices assessment, Entra authentication, extended security update delivery, and the hybrid benefit toggle that lets you change license type with no downtime at all.

Its permission model was reworked a few years ago into a per-feature, least-privilege arrangement, which is worth mentioning mostly to head off an old habit: the previous model of choosing a broad management mode is gone, and any guidance describing it is out of date. A self-installed modern SQL Server on an Azure virtual machine will register itself, and registration can also be done manually or in bulk across a subscription. My standing advice is to register everything, deliberately, as an inventory exercise rather than a feature decision, because an unregistered SQL Server virtual machine is invisible to every management surface that would otherwise tell you it exists and is unpatched.

One deprecation matters on a timeline you can plan around. The original automated patching feature is on its way out with a hard retirement date in September 2027, and the replacement is Azure Update Manager. This is a rare case where the replacement is unambiguously better rather than merely different, and the documentation says why in one sentence: unlike automated patching, Update Manager installs cumulative updates for SQL Server. The old feature handled Windows updates and important SQL updates but never the cumulative updates, which is precisely the thing a database administrator cares about. If you are still on the old feature, moving is not a chore, it is an upgrade.

Storage is where these builds are won and lost

Nothing else you configure on a SQL Server virtual machine will move performance as much as the storage layout, and nothing else is as consistently done by habit. The durable guidance has been stable for years and is worth restating because it still gets missed: data files on disks with read-only caching, log files on disks with no caching at all, tempdb on the machine’s ephemeral local disk where it belongs, and volumes formatted with a 64 kilobyte allocation unit. Those four decisions, made correctly at build time, are worth more than a tier upgrade.

On disk type, be careful with the advice you have absorbed recently. Premium SSD v2 is genuinely attractive, because it decouples capacity from performance and lets you buy the throughput you need without buying a disk you do not, but its portal support is still narrow, scoped to specific virtual machine series, and the page describing that path still carries a preview label. It is a conditional recommendation rather than the new universal default, and the conventional premium tiers remain the general-purpose answer for data drives. Ultra Disk stays where it has always been: the option for a log file that genuinely needs sub-millisecond latency, chosen because you measured it rather than because it sounded fast.

High availability, and the load balancer you no longer need

Availability groups across availability zones are the standard shape, with failover cluster instances remaining available for the workloads that require shared storage semantics, built on Azure shared disks, Storage Spaces Direct, or a premium file share.

The genuinely useful development, and the one that most estates have not caught up with, is the multi-subnet availability group. Placing each replica in its own subnet and giving the listener an address in each one removes the internal load balancer entirely, and removes the need for the distributed network name listener that was invented to work around the load balancer’s behaviour. The client library does the work, given the right connection string option. This is simpler, faster to fail over, and easier to reason about than what most people built five years ago. Two caveats keep it honest. Microsoft describes it as recommended rather than as the default, and the scripted deployment paths, the command line and the quickstart templates, still build the single-subnet arrangement with a load balancer. So it is the design to choose, and choosing it means not letting a template choose for you. The distributed network name listener has not gone away and remains the correct fix when a single subnet is genuinely unavoidable.

For disaster recovery across regions, the native database technologies beat the infrastructure ones, and Microsoft’s own comparisons say so. A cross-region or distributed availability group gives better recovery point and recovery time behaviour than replicating the whole machine, and machine-level replication carries a specific trap worth remembering: it does not coexist with transactional replication topologies. Log shipping remains unglamorous, entirely supported, and the right answer more often than its reputation suggests, particularly for older sources.

The license is the bill

Here is the number that reorganises how people think about this option. On a mid-sized general-purpose virtual machine, four vCPUs running Windows in a typical region, the machine costs a little under three hundred dollars a month. SQL Server Standard licensing on that same machine, billed hourly through the marketplace, costs slightly more than the machine does. Standard is a little over half the total bill. Enterprise is roughly four times the cost of the virtual machine, so the machine becomes a rounding error next to the software running on it.

Two consequences follow, and both are actionable. First, right-sizing the virtual machine is a licensing exercise, not a compute exercise: cores are what you pay for twice. Second, there is a four-core floor on the license, so shrinking a machine below four vCPUs saves compute and saves absolutely nothing on SQL Server. If you have a fleet of small database servers on two-core machines, they are being billed as four-core machines for the part of the bill that matters, and consolidating them onto fewer, larger instances is usually the single largest saving available.

This is also where existing licensing becomes decisive rather than interesting, and why it gets its own article later. If you hold licenses with active Software Assurance you can apply them here and switch the machine’s license type with no downtime, and you gain passive replica rights that make a properly designed availability group substantially cheaper than the naive arithmetic suggests. If you do not, you are paying the hourly meter on every core, and the conversation about whether a platform service is cheaper stops being close.

Nobody ever regretted asking what the existing licenses entitle them to before designing the target. Plenty of people have regretted the order in which they did it.

When a pinned version is the right call

There are four situations where I recommend this option without hesitation, and none of them are about performance.

The first is a workload that needs the parts of the product the platform services do not run: Reporting Services, Analysis Services, or a feature outside what the managed offerings implement. The second is a vendor certification that names a version and a build, where running anything else is unsupported by the people you would be calling. The third is a regulated system where any engine change triggers a revalidation cycle, and the ability to decline an update is worth more than the effort of applying them yourself. The fourth is simple scale: a database beyond what the platform tiers hold, where a machine with a large enough pile of disks is the only thing that fits.

Note what is not on that list. Familiarity is not a reason. Neither is a general preference for control, unexamined. If you cannot articulate which specific authority you need to retain and why, the honest recommendation is a managed instance, and the virtual machine is costing you operational work you are not being paid for.

One forward-looking detail worth carrying, because it connects two parts of this series: several capabilities introduced in the newest release of SQL Server require the machine to be Arc-enabled, including managed identity authentication and the newer key management integrations. Arc has stopped being purely a management story for machines outside Azure and has started being a prerequisite for product features. That is a direction, not an accident, and it makes the Arc article later in this series relevant even to estates that are entirely inside Azure.

What the build sheet does

The companion build sheet migrates a genuinely old estate onto a current SQL Server virtual machine, using the portal migration experience that reached general availability a few weeks ago as its spine and the manual backup-to-URL path as the equivalent for anyone who cannot use it. It registers the extension, sets the license type deliberately, lands the database at its original compatibility level, and captures the baseline before anything is raised. It is the least exotic migration in this series and the one most estates will actually perform.


Azure SQL
‹ Previous: [SQL 3.1] Build Sheet: Migrating to Managed Instance
Next: [SQL 4.1] Build Sheet: Migrating to a SQL Server Azure VM