If you manage Azure SQL Database resources through ARM templates, the Azure SQL REST API, the Az.Sql PowerShell module on an old version, the Azure CLI shipped with an older deployment image, or any third-party tooling that wraps the Azure Resource Manager surface, you have eight days of runway when this lands. Microsoft is retiring the entire 2014-04-01 API version of the Azure SQL Database control plane on June 30, 2026.
The original retirement date was October 31, 2025. Microsoft extended it by eight months "based on customer feedback," which means a lot of estate still has 2014-04-01 baked into it.
The loud failure mode is straightforward: ARM templates that pin "apiVersion": "2014-04-01" start failing at deploy time. You get a clear error, you bump the version, you redeploy. That's not the failure mode this article is about.
The interesting failure mode is what happens when your tooling auto-upgrades to the newer stable version (2021-11-01) without you noticing. Microsoft itself describes this as the benign path:
The retirement of the 2014 API will not affect Azure PaaS or SaaS products that are kept up to date, as the API will be updated automatically.
What the announcement does not spell out is that the upgrade from 2014-04-01 to 2021-11-01 is not a transparent version bump. It's seven years of API redesigns crammed into a single rename. Endpoints disappear without a replacement. Resource types are renamed. Audit policies move from one storage backend to another. The most common automation patterns built against 2014-04-01 will continue to "succeed" against 2021-11-01 — they just stop reading the thing they think they're reading.
The mappings are spelled out in Microsoft's own retirement notice. Here are the five silent surfaces that fall out of the differences between the two versions, with what each one does to the kind of script you probably have running.
1. Eight endpoint groups have no replacement at all
The retirement table lists thirty-odd 2014-04-01 endpoint groups mapped to their 2021-11-01 successors. Eight of them have a footnote that reads: "APIs marked as deprecated with no newer/stable versions, are permanently discontinued from the service."
- Database Connection Policies
- Elastic Pool Activities
- Elastic Pool Database Activities
- Queries
- Query Statistics
- Query Texts
- Recommended Elastic Pools
- Service Tier Advisors
If you have a script that polls /queries, /queryStatistics, or /queryTexts against the 2014-04-01 API to surface query performance data outside of the portal — the SDK or tooling layer above you cannot transparently retry against 2021-11-01, because the endpoint isn't there. Whatever your tooling does when an endpoint disappears, that's what it does on July 1. Most SDKs raise. Most schedulers log the raise and continue. The dashboard the script was feeding goes flat. Nobody notices because the job ran "successfully" — it raised, logged, and exited zero, exactly as it was written to.
Recommended Elastic Pools is the sneakiest of the eight, because the recommendation it surfaced was advisory. If your pool-sizing job was using it to decide whether to scale or merge pools, the job continues to make sizing decisions — based on an empty recommendation set. Everything looks "stable" because there's nothing to act on.
2. Table-based auditing silently maps to blob-based auditing
Database Table Auditing Policies (2014-04-01) maps to Database Blob Auditing Policies (2021-11-01). Same for the server-level variant.
These are not the same audit subsystem. Table auditing wrote audit rows to Azure Table Storage in the same storage account. Blob auditing writes audit logs to Blob Storage with a different filename convention, different retention semantics, and a different schema. The legacy table-auditing destination has been deprecated for years, but the API surface persisted in 2014-04-01 as a way to enable, disable, and read the policy.
Tooling that reads "is auditing enabled" against the new endpoint gets a different answer for the same logical question. Concretely:
- A compliance script that did
GET .../auditingPolicies/Default?api-version=2014-04-01and asserted onproperties.auditingState == "Enabled"is now hitting.../auditingSettings/default?api-version=2021-11-01. The property layout is different. If the tool was tolerant — e.g. checkedif (auditing.properties.state == 'Enabled')against a value that doesn't appear in the new shape — it returnsundefined, the check evaluates falsy, and you report "auditing not enabled" for databases that very much have blob auditing enabled. - A provisioner that wrote table-auditing config via 2014-04-01 (
PUT .../auditingPolicies/Default) is now writing blob-auditing config (PUT .../auditingSettings/default). The request shape is similar enough that a JSON template with the wrong keys can still 200, but the resulting policy is partially configured, pointed at the wrong storage container, or missing the retention period. Your IaC layer reports a successful apply.
Either way, the audit you thought you had is not the audit you have, and the surface that would tell you is the surface that just changed.
3. Threat Detection Policies were renamed to Advanced Threat Protection Settings
Database Threat Detection Policies (2014-04-01) maps to Database Advanced Threat Protection Settings (2021-11-01).
The 2014 endpoint exposed properties like state, emailAddresses, emailAccountAdmins, and disabledAlerts. The 2021 endpoint replaces the alert/email config with a simpler state toggle and pushes the alert configuration into Defender for Cloud at the subscription tier.
A security posture script that reads the old endpoint's disabledAlerts array — say, to ensure SQL injection alerts haven't been muted — has nothing analogous to read on the new endpoint. The migration broke the very signal the script existed to check. The script still runs. It still produces a report. The report just doesn't include the field it was built around, and depending on how it was written, either renders the column empty or silently treats "no muted alerts" as the answer.
4. Disaster Recovery Configurations became Failover Groups
Disaster Recovery Configurations (2014-04-01) → Failover Groups (2021-11-01).
This isn't a rename for cosmetic reasons. Failover Groups are the modern, multi-database DR primitive. Disaster Recovery Configurations were a per-database active geo-replication wrapper with a different lifecycle, different role semantics ("Primary"/"Secondary"/"Source"/"Target" depending on which way you looked at it), and a different relationship to the underlying replicas.
If you have a DR posture monitor that does something like:
for each server, list
disasterRecoveryConfigurationchildren, count where role == "Primary," compare against expected_count, alert if delta
…you are reading an empty list on the new endpoint, because nothing creates DR configurations on a 2021-vintage server. Everything created in the last few years is a failover group, and failover groups live at a different URL with a different resource type. The script returns zero. The script's invariant is "zero is bad." But the script doesn't fire, because the script's invariant was actually "zero compared to last week's count is bad" — and last week's count was also zero, since the script has been wrong since the auto-upgrade happened. It looks healthy by virtue of being uniformly broken.
The unambiguous version of this problem is the IaC tool that, on next plan, decides to delete the disasterRecoveryConfiguration child because "it's not in the template anymore." It isn't in the template anymore because the template now references failoverGroups. The DR resource itself doesn't get deleted (the underlying replica's data lives elsewhere), but the management plane object pointing at it does, and reattaching from scratch is the kind of thing you find out about during a real outage.
5. Restorable Dropped Databases now points at managed instances
The most quietly dangerous entry in the migration table:
Restorable Dropped Databases (2014-04-01) → Restorable Dropped Managed Databases (2021-11-01).
Read that carefully. The replacement is the managed instance variant, not the logical-database variant. If you had a self-service "undelete a dropped database" tool that called the 2014 endpoint against your standard Azure SQL Database (not managed instance), and that tool got auto-upgraded by an SDK bump or a wrapper-library bump, it is now asking the API for restorable dropped managed databases. For a standard Azure SQL server, the list is empty.
The runtime behavior:
- User drops a database, panics, opens the self-service restore tool.
- The tool calls the upgraded endpoint, asks for restorable dropped databases on the managed-instance resource path.
- The managed-instance resource doesn't exist on a standard logical server, or the list returns empty for the correct managed instance.
- The tool reports "no restorable databases found."
- The user assumes the drop was permanent and moves on.
The actual restore endpoint for a standard logical server is restorableDroppedDatabases under Microsoft.Sql/servers/restorableDroppedDatabases — which still exists in 2021-11-01, just not in this row of the migration table. Microsoft's own retirement-mapping doc only lists the managed-instance variant under the 2014 row, because the standard variant has been promoted to the server-scoped resource model. If your tool didn't notice the model shift, the migration documentation actively misleads.
How to find what's affected before June 30
Three commands to run in your environment this week.
Inventory ARM templates and Bicep: grep -rE '"apiVersion"\s*:\s*"2014-04-01"' templates/ infra/ (or the moral equivalent in Bicep) catches the loud cases. Use the Azure Resource Graph to see deployed resources by API version if you have it enabled.
Inventory SDK and CLI versions: the Azure SQL SDKs (Azure.ResourceManager.Sql in .NET, azure-mgmt-sql in Python) shipped major versions between the 2014 and 2021 API generations. A pip list | grep azure-mgmt-sql or its NuGet/npm equivalent will show whether you're on a version that pins old APIs. The Az.Sql PowerShell module has the same property — Get-Module Az.Sql | Format-List Version tells you whether your runners are still on a pre-5.x build.
Inventory custom REST callers: if anything in your environment talks directly to management.azure.com/subscriptions/.../providers/Microsoft.Sql/...?api-version=2014-04-01, that's the highest-confidence place to look first. Network egress logs, an az monitor activity-log list over the past 30 days, or just a grep across your repos for Microsoft.Sql/ paths will surface them.
For each find, the migration is mechanical when the endpoint maps cleanly (most databases, servers, firewallRules calls), and a real design decision when it lands on one of the five surfaces above. Make the design decision now while you have a fallback period, not after July 1.
The published retirement notice is here: Azure SQL Database REST API 2014-04-01 Retirement Notice. The full 2021-11-01 surface is documented in the same place — read it side-by-side with whatever your tooling is currently using.
FlareCanary watches the response shapes of API endpoints you depend on and tells you when they drift. The Azure SQL control plane is exactly the kind of surface where renamed properties and silently-substituted endpoints land in production before a deployment-time error ever fires. flarecanary.com
Top comments (0)