By the end of this build sheet every SQL Server instance in a mixed estate is projected into Azure as its own resource, carries a license type somebody deliberately chose rather than one that was inferred, has a free migration assessment running weekly against it, and, where it is out of support, has extended security updates subscribed from the correct date rather than from whenever somebody got round to it. You also have a set of Resource Graph queries that prove all of that on demand, which is the part that survives your involvement.
Prerequisites: the servers already onboarded to Azure Arc, which is a separate exercise covered in the Arc onboarding article and is a genuine trust decision rather than an install step; Contributor on the resource groups holding the Arc machines plus the ability to write tags at subscription or resource group scope; instances running SQL Server 2012 or later on 64-bit Windows, noting that Windows Server 2012 R2 hosts lack the telemetry endpoint the assessment and dashboards depend on; an answer from whoever owns the licensing agreement about what each server is actually entitled to, because step 3 is where guessing becomes expensive; and the Azure CLI with the resource-graph extension available.
Naming and tagging convention
The SQL instance resources are named by the platform from the machine and instance names, so the convention that matters here is the tagging one. Use SqlLicensePosture with values matching what you set, so that a human reading the resource can see the intent alongside the property, and SqlEstateWave with a wave number for migration planning. The exclusion tag is not yours to name: it is ArcSQLServerExtensionDeployment and it takes the exact value Disabled. The same tag does double duty: with the values Paid, PAYG, PAYG-Recurring or LicenseOnly it sets the license type at onboarding instead, and the automatic workflow checks it at subscription, then resource group, then resource scope. The worked example uses a resource group called rg-vcio-arc-servers.
Step 1: Decide what should NOT be onboarded, first
This step comes first because the default is automatic. Once a machine is Arc-connected and running SQL Server, the Azure extension for SQL Server deploys to it without being asked. That is usually what you want, and there are two cases where it is not: a server whose SQL Server is a vendor appliance you are contractually not permitted to modify, and a development machine whose instances would otherwise start appearing in your licensing position.
Exclusion is a tag, applied at subscription, resource group, or individual machine scope:
# Exclude an entire resource group of appliance servers
az tag update \
--resource-id "/subscriptions/$SUB/resourceGroups/rg-vcio-arc-appliances" \
--operation merge \
--tags ArcSQLServerExtensionDeployment=Disabled
Expected result: the extension does not deploy to machines in that scope, and machines that already have it are unaffected, because this governs deployment rather than removal. Failure mode, and it generates more support calls than anything else in this article: the tag value is cached, and the documentation states that changes can take up to eight hours to take effect. If you set the exclusion and the extension appears anyway, you are almost certainly inside that window rather than looking at a fault. Set exclusions before you onboard the machines, not after, and you will never meet this.
Step 2: Confirm the extension landed and is supported
The component is the Azure extension for SQL Server, published by Microsoft.AzureData with type WindowsAgent.SqlServer on Windows, and it projects each instance as a resource of type Microsoft.AzureArcData/SqlServerInstances. Those are the durable nouns; learn them, because they are what you query on.
az connectedmachine extension list \
--machine-name SQL2019-OPS01 \
--resource-group rg-vcio-arc-servers \
--query "[?properties.type=='WindowsAgent.SqlServer'].{name:name, ver:properties.typeHandlerVersion, state:properties.provisioningState}" \
-o table
Expected result: one extension, provisioning state Succeeded, with a version. Note that properties.typeHandlerVersion is the requested version and properties.instanceView.typeHandlerVersion is what is actually installed, and Microsoft’s own sample queries use the latter. If any of the estate runs SQL Server on Linux, add LinuxAgent.SqlServer to the filter or those hosts will silently not appear. Now do the thing almost nobody does, which is check that version against the support window. The documentation is unambiguous: only versions released within the last year are supported. The management plane has its own patching obligation, and an estate running an eighteen-month-old extension is unsupported for the tooling that is supposed to be telling it what is unsupported. Put a recurring review on this.
Step 3: Set the license type deliberately on every instance
This is the substantive work and everything commercial depends on it. The license type decides what the instance costs and which features are available at all.
| Setting | Value | Why |
|---|---|---|
| Covered by core licenses with active Software Assurance or a subscription | Paid | Unlocks extended security updates, the best practices assessment, automatic updates, monitoring and upgrade rights at no additional charge. Automatic license detection will set this itself when it can see the entitlement. |
| Buying the license from Azure by the hour | PAYG | Bills per core hour with a four-core minimum per operating system environment. Rates are identical to the SQL Server license meters on an Azure virtual machine, so location does not change the price. |
| Buying hourly through a partner | PAYG, onboarded with the PAYG-Recurring tag | In the CSP channel, consent to recurring pay-as-you-go billing has to be registered. That is done with the value PAYG-Recurring on the ArcSQLServerExtensionDeployment tag during automatic onboarding, which records a ConsentToRecurringPAYG property on the extension. It is not a value of the license type property itself, and setting it as one will be rejected. Once registered, the consent cannot be changed without reinstalling the extension. |
| Licensed by server plus client access licenses, or core licenses without Software Assurance | LicenseOnly | The trap worth memorising: if you license by server and CAL you must use this value even if you hold active Software Assurance. The intuition that Software Assurance always means Paid is wrong in that case, and the documentation says so explicitly. |
Find the instances that have no license type at all, which on a freshly onboarded estate is usually more of them than anyone expects:
az graph query -q "
resources
| where type =~ 'microsoft.azurearcdata/sqlserverinstances'
| extend licenseType = tostring(properties.licenseType),
version = tostring(properties.version),
edition = tostring(properties.edition)
| where isempty(licenseType) or licenseType == 'Undefined'
| project name, resourceGroup, version, edition, licenseType
| order by name asc" --first 500 -o table
Expected result: ideally an empty set. Anything returned is an instance whose commercial position nobody has stated, and each one is a line in an audit you cannot answer. Set them explicitly:
# License type is a setting on the Azure extension for SQL Server on the
# Arc machine, not a property of the projected SqlServerInstances resource.
# Warning: --settings OVERWRITES the whole settings blob. If the machine has
# an exclusion list or an ESU flag, restate them in the same command.
az connectedmachine extension update \
--machine-name SQL2019-OPS01 \
--resource-group rg-vcio-arc-servers \
--name "WindowsAgent.SqlServer" \
--type "WindowsAgent.SqlServer" \
--publisher "Microsoft.AzureData" \
--settings '{"LicenseType":"Paid", "SqlManagement": {"IsEnabled":true}}'
Set expectations correctly on the at-scale story, because this is where guidance elsewhere overpromises. There is no built-in Azure Policy that assigns license type. The documented way to remediate a large estate is an open-source script sample Microsoft publishes, modify-license-type.ps1, driven from the Resource Graph query above and best run from Cloud Shell. That works perfectly well; it is just not the policy-driven governance people assume exists. Be precise about the boundary, though, because there is one built-in policy in this area: “Subscribe eligible Arc-enabled SQL Servers instances to Extended Security Updates” will enrol instances at scale, but only ones whose license type is already Paid or pay-as-you-go, which is exactly why this step comes first.
One commercial effect worth raising with whoever owns the security budget, because it is unadvertised and occasionally pays for the exercise. Microsoft’s Arc SQL overview says only that “When you enable Microsoft Defender through SQL Server enabled by Azure Arc, you can get substantial cost savings on Defender”, and the one billing-unit statement on Learn is that Defender for SQL Servers on Machines is billed per server rather than per database. The finer claim, that servers outside Azure which are not Arc-enabled are priced per vCore while Arc-enabled ones are priced per instance, appears on the pricing pages rather than in the documentation, so verify it against current pricing for the client’s agreement before you put a number in a proposal. If it holds, onboarding a core-heavy server moves it between billing units, and on a fleet of large physical servers already covered by Defender that can be a material reduction rather than an addition.
Step 4: Turn on the assessment and read it properly
The migration assessment is free at every license type, including LicenseOnly, which makes it the single strongest reason to do any of this. It runs automatically on a weekly cadence rather than waiting to be invoked, and it evaluates readiness per database plus a sizing recommendation drawn from up to thirty days of performance history at the ninety-fifth percentile.
Its constraints are documented and worth knowing before you promise coverage: Windows only, no failover cluster instances, SQL Server 2012 and later, and not on Windows Server 2012 R2 hosts. The assessment page states the exclusion without a reason; the Arc SQL overview page supplies it, that “Windows Server 2012 R2 doesn’t support the telemetry endpoint. Therefore features such as performance dashboard, migration assessment, and others aren’t supported.” On a typical legacy estate that last exclusion removes exactly the servers you most wanted assessed, so check host operating systems early.
Reading the output well comes down to three habits. First, wait for a full thirty days before treating the sizing as authoritative; an assessment run in its first week is sizing from a few days of data and will be wrong in whichever direction that week was unusual. Second, use the hybrid benefit and reservation toggles, because an estimate that ignores the licenses the client already owns is not an estimate of anything they will pay. Third, and this is the caveat to keep out of the client’s spreadsheet, the price estimates are computed on a fixed basis: West US, US dollars, and 732 hours a month. It is excellent for comparing targets against each other and unreliable as an absolute budget figure. Rebuild the numbers for the client’s region and agreement before they go in a proposal.
Step 5: Subscribe extended security updates from the right date
Extended security updates are gated behind Paid or one of the pay-as-you-go types, so step 3 has to be right before this will work at all. The subscription is per instance and can be cancelled when the instance is migrated or decommissioned, which is the whole advantage over a volume licensing arrangement.
The behaviour that decides your timing, and the reason this step exists as its own section: subscribing late bills back to the beginning of the current extended update year, based on the timestamp when the subscription was enabled. Waiting does not save money. It accumulates a larger invoice and delays the patches. For SQL Server 2016 the term began the day support ended, 14 July 2026, so an instance still running now is already accruing whether or not anyone has subscribed it. And if you start the subscription after the end-of-support date, Microsoft requires you to cover any previous years through volume licensing separately.
Two things to check before you subscribe anything. Whether the instance is actually going to still be running when the updates arrive, because subscribing something you are decommissioning next month is pure waste. And whether it is SQL Server 2014, which is explicitly grandfathered and still receives its updates free on Azure virtual machines, unlike everything from 2016 onward. The Arc licensing article covers the mechanics of the enrolment flow, and the same shape applies here.
Step 6: Know what you have not switched on
An honest build sheet says where the product stops. What you have just switched on divides along the license type you set in step 3, and it is worth having this in front of you when somebody asks why a feature is greyed out on one server and not another.
| Capability | Available at | Why it matters here |
|---|---|---|
| Instance and database inventory | Every license type, including LicenseOnly | The reason to onboard an estate you have made no decisions about. Free, and it is what the validation queries in step 7 read. |
| Migration assessment and the portal database migration flow | Every license type | Readiness plus sizing from up to 30 days at the 95th percentile, refreshed weekly. The strongest single justification for this build sheet. |
| Microsoft Entra authentication | Every license type, SQL Server 2022 and later only | Brings on-premises instances into the same identity model as the rest of the estate. The version floor rules out most legacy servers. |
| Defender for SQL and Purview data map scanning | Every license type | Not license-gated, though Defender is separately billed. Note the billing unit change described in step 3. |
| Extended security updates | Paid or a pay-as-you-go type only | The reason step 3 has to be right before step 5. A LicenseOnly instance cannot subscribe, which is exactly the situation the worked example ran into. |
| Best practices assessment, automatic updates, upgrade rights, unlimited virtualization | Paid or pay-as-you-go | The management value that active Software Assurance or an hourly license buys you at no further cost. |
| Automated backups, point-in-time restore, monitoring dashboards | Automated backups and point-in-time restore: Paid or pay-as-you-go. Monitoring is not listed in the license-type matrix at all, so do not promise it is gated the same way. All three are still preview. | Evaluate them, do not depend on them. Monitoring pricing after general availability is explicitly undetermined, which is a direct instruction not to build a cost model on it. |
Automatic updates deserve a specific warning because the name oversells them. They cover important and critical updates only, and the documentation is explicit that you must still manually install other updates such as service packs and cumulative updates. If somebody has been told that Arc will handle SQL Server patching for the estate, that is not what this does, and the correction is much cheaper delivered now than during a patch review.
What you have genuinely gained is inventory, licensing posture as queryable metadata, a continuously refreshed migration assessment, extended security updates on demand, Entra authentication for SQL Server 2022 and later, and Defender integration. That is a strong free tier. It is not an operations platform yet.
Worked example: a mixed estate, the week after 2016 went out of support
The estate as found: fourteen SQL Server instances across eleven servers. Three on SQL Server 2014 Standard, six on 2016 Standard, four on 2019 Standard, one on 2019 Enterprise. Two of the 2016 servers run Windows Server 2012 R2. One 2014 instance is a vendor appliance nobody is permitted to modify. Licensing: core licenses with Software Assurance for the 2019 estate, server-plus-CAL for the older boxes, which nobody had written down anywhere.
What was done, in order. The appliance’s resource group was tagged for exclusion before onboarding, so the extension never deployed there. The remaining ten servers were onboarded and the extension landed automatically. The Resource Graph query returned nine instances with no license type, which was more than expected and was the moment the client realised the licensing position genuinely was not documented anywhere.
The license types were then set from the actual entitlements rather than from the pattern. The 2019 estate went to Paid, correctly, on core licenses with Software Assurance. The 2014 and 2016 boxes went to LicenseOnly, and this is the part worth dwelling on, because the instinct in the room was to set them to Paid on the grounds that Software Assurance existed somewhere in the agreement. They were licensed server-plus-CAL, and that mandates LicenseOnly regardless of Software Assurance. Setting those six instances to Paid would have been a misstatement of the licensing position recorded as Azure metadata, which is precisely the sort of thing that is worse than having no record at all.
The consequence landed immediately and was uncomfortable: LicenseOnly does not unlock extended security updates. The six 2016 instances, now out of support, could not subscribe until their licensing changed. That is not a limitation of Arc, it is Arc surfacing a licensing reality that had been invisible, and it moved a conversation with the reseller from “sometime this year” to that week. The two instances on Windows Server 2012 R2 also dropped out of the assessment entirely, which reduced the scope of the migration plan and was better known in week one than in month three.
Six weeks later the assessment had a full performance history on the eight eligible servers, and it recommended a managed instance target for five of the databases and General Purpose sizing well below what the on-premises hardware suggested, because the servers had been specified for a peak that stopped happening in 2019. That single finding, which cost nothing and required no consultant, changed the shape of the proposal more than any other input.
Step 7: Validate with queries you can re-run
The point of this build sheet is a state you can prove on demand rather than a task somebody completed once. Keep these.
# Every instance, its version, edition and stated licence posture
az graph query -q "
resources
| where type =~ 'microsoft.azurearcdata/sqlserverinstances'
| extend licenseType = tostring(properties.licenseType),
version = tostring(properties.version),
edition = tostring(properties.edition),
status = tostring(properties.status)
| project name, resourceGroup, version, edition, licenseType, status
| order by version asc, name asc" --first 500 -o table
# Anything still unstated: this must return nothing
az graph query -q "
resources
| where type =~ 'microsoft.azurearcdata/sqlserverinstances'
| extend licenseType = tostring(properties.licenseType)
| where isempty(licenseType) or licenseType == 'Undefined'
| summarize unstated = count()" -o table
# Out-of-support versions, which is the migration and ESU worklist
az graph query -q "
resources
| where type =~ 'microsoft.azurearcdata/sqlserverinstances'
| extend version = tostring(properties.version),
licenseType = tostring(properties.licenseType)
| where version in ('SQL Server 2012','SQL Server 2014','SQL Server 2016')
| project name, resourceGroup, version, licenseType
| order by version asc" -o table
Expected results: every instance present with a version and an edition; zero unstated license types; and an out-of-support list that matches what you believe you have. That third query is the one to run monthly, because it is the list that decides both the migration order and the extended security update spend, and it maintains itself.
The end-to-end test that proves the whole chain rather than its parts: pick one out-of-support instance, confirm it appears in the third query with the license type you set, open its assessment and confirm a readiness result and a sizing recommendation exist for its databases, then confirm its extended security update status matches the decision you made in step 5. If all three are true for one instance, the pipeline from onboarding through licensing to assessment to patching works, and the rest is repetition.
What comes next
Let the assessment accumulate a full thirty days before anyone makes a target decision from it. Put the extension version review and the out-of-support query on a monthly cadence. Then take the assessment’s recommendations into the destination build sheets: Azure SQL Database for the databases with no instance dependencies, Managed Instance for the ones that need the Agent and cross-database queries, and a virtual machine for whatever needs the whole product. The estate that stays where it is now has an inventory, a defensible licensing position, and a migration plan that updates itself every Sunday, which is a better place to be than most estates that did decide to move.
Azure SQL
‹ Previous: [SQL 5] Arc-Enabled SQL Server: Managing What Stays
Next: [SQL 6] Licensing and Cost: AHB, ESUs, and Where the Money Goes ›




