Hardening Azure SQL: the basics
What does a SQL database with no threat detection actually look like?
An Azure SQL logical server and a SQL Managed Instance both hold some of the most sensitive data in a subscription, and both ship with Advanced Threat Protection switched off until someone turns it on. Advanced Threat Protection is the detection layer of Microsoft Defender for SQL: when it is enabled, the platform watches for a defined set of threat types, potential SQL injection, access from an unusual location or data centre, access from an unfamiliar principal or potentially harmful application, and brute force on SQL credentials, and raises a security alert when it sees one. When it is off, those same events happen silently. The data is still reachable, the credentials are still brute-forceable, the injection attempt still lands, but nothing tells you.
Microsoft Defender for Cloud turns this into a recommendation, 'All advanced threat protection types should be enabled in SQL server advanced data security settings', and a parallel one for SQL Managed Instance. The two read as separate findings because a logical server and a Managed Instance are different resource types, but they are the same job: make sure the threat detection is on, and on for every type, across every SQL resource in the estate. There is no partial-credit position here. A server with detection half-configured is a server where a real attack can slip through the gap.
Most of this is omission, not intent. A logical server provisioned before the team standardised on Defender for SQL, a Managed Instance spun up by a module that never set the plan, a subscription where the Defender for Azure SQL plan was simply never switched on. The work is to find every SQL server and Managed Instance where Advanced Threat Protection is not fully enabled, switch it on, and then enable the Defender for Azure SQL plan at the subscription level so that every database created from now on is covered automatically.
In this lesson you will learn what Advanced Threat Protection detects on Azure SQL Database and SQL Managed Instance, how to find every SQL resource where it is not fully enabled, and how to switch it on per resource and then guarantee coverage for the whole subscription with the Defender for Azure SQL plan. The Controls this lesson covers section lists every Defender for Cloud recommendation in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.
The injection that announced itself
SQL injection has topped the list of web application risks for the better part of two decades, and the reason it persists is that a single unparameterised query can hand an attacker the contents of a database. Advanced Threat Protection on Azure SQL was built precisely to catch this class of attack as it happens: when it spots a query that carries the signature of an injection attempt, it raises an alert with the suspicious SQL statement and the source, often the first signal a team gets that an application has a hole in it. The detection is only useful, though, if it is switched on. On a server where Advanced Threat Protection was never enabled, that same injection attempt produces no alert at all.
Finding SQL servers with threat detection off
Priya is the security lead at a fintech preparing for a SOC 2 audit. Defender for Cloud shows advanced-threat-protection findings across several Azure SQL logical servers and a couple of Managed Instances that pre-date the team's current Defender baseline.
Rather than open each resource in the portal, Priya starts by querying which logical servers still have Advanced Threat Protection disabled, so the gap is a concrete list before anything is changed.
List every Azure SQL logical server and read back its Advanced Threat Protection state. Anything not 'Enabled' is running blind.
The state value is one of Enabled, Disabled or New. Any logical server or Managed Instance showing anything other than Enabled is a server where an injection or brute force attempt would raise no alert. Prioritise the ones holding sensitive data.
How Defender for Cloud decides SQL threat protection is missingdeep dive
The recommendation reads the Advanced Threat Protection setting on the resource. On a logical server this is the 'Microsoft.Sql/servers/advancedThreatProtectionSettings' resource named 'Default', and on a Managed Instance the equivalent under 'Microsoft.Sql/managedInstances'. Its 'state' property is one of three values: 'Enabled', 'Disabled', or 'New' for a resource where a state has never been applied. The recommendation is satisfied only when state is 'Enabled'; both 'Disabled' and the unset 'New' state count as a failure. The phrase 'all advanced threat protection types' is historical: earlier versions let you select a subset of threat types, so the control checks that detection is on for every type, not a chosen few. In the current model enabling Advanced Threat Protection enables the full set, which is why the modern goal is simply state 'Enabled'.
There is a layer above the per-resource setting. Microsoft Defender for SQL is a Defender for Cloud plan billed per protected resource. When you enable the Defender for Azure SQL plan at the subscription level, it automatically turns on Advanced Threat Protection and vulnerability assessment for every Azure SQL database in that subscription, including ones created later. The per-resource setting and the subscription plan are two views of the same capability: you can switch detection on one resource at a time, but the plan is what makes coverage automatic and durable.
One trap to avoid: there was a built-in Azure Policy, 'Advanced Threat Protection types should be set to All in SQL server Advanced Data Security settings', that audited this. It is deprecated and should not be assigned. The supported way to enforce the capability at scale is the Defender for Cloud plan, assigned through the built-in 'Configure Azure Defender to be enabled on SQL servers and SQL Managed Instances' policy or simply by enabling the plan on the subscription. Reaching for the old deprecated policy is a common and avoidable mistake. Note too that Defender for Cloud assesses against the Microsoft Cloud Security Benchmark on a periodic cycle, so the recommendation does not flip to Healthy the instant you enable detection; the control-plane change is immediate but the posture catches up on the next assessment, which matters when you are gathering audit evidence to a deadline.
What is the impact of leaving SQL threat protection off?
The direct impact is blindness to attacks aimed squarely at your data. With Advanced Threat Protection off, a SQL injection probe against an application, a brute force run against database credentials, access from an unfamiliar principal or an unusual data centre, and access patterns that look like exfiltration all happen without raising an alert. The events still occur; what is missing is the signal. The whole value of detection is shrinking the window between when an attack starts and when someone notices, and that window is effectively unbounded on a database where detection was never switched on.
The second-order impact is incident response that starts cold. When a breach is eventually discovered through some other channel, a customer report, a credential found for sale, the team has no alert trail to reconstruct what happened or when it began. Advanced Threat Protection alerts give an investigation a starting point: the suspicious statement, the source, the timestamp. Without them, scoping the breach is slower, more expensive, and far less certain, which directly increases the cost and duration of every notification and forensics obligation that follows.
On the compliance side, every modern framework, ISO 27001, SOC 2, HIPAA, PCI DSS and the UK and EU data-protection regimes, expects evidence that threats to data stores are actively monitored and detected, not just that access is controlled. A passing set of SQL advanced-threat-protection recommendations, backed by the Defender for Azure SQL plan across every subscription, is a clean and defensible artefact that detection is genuinely in place.
How do you enable SQL threat protection safely?
Work the capability as one loop rather than chasing individual findings. Enabling Advanced Threat Protection is a detection-only change: it adds monitoring and alerting and does not block, throttle or alter any query, so there is no risk of breaking a live application. The order is about coverage and cost, not safety.
1. Inventory every SQL resource by detection state
List every Azure SQL logical server and SQL Managed Instance and read back the Advanced Threat Protection state on each. Treat this inventory as the source of truth, not the Defender finding count, because logical servers and Managed Instances surface as separate recommendations even though they are the same capability.
2. Enable detection on the highest-value databases first
Set Advanced Threat Protection to Enabled on each resource, prioritising the servers and Managed Instances that hold the most sensitive data, payments, customer records, anything regulated. This is purely additive: detection switches on, nothing about the database's behaviour or availability changes.
3. Turn on the Defender for Azure SQL plan for the subscription
Enable the Defender for Azure SQL plan at the subscription level. This automatically enables Advanced Threat Protection and vulnerability assessment for every Azure SQL database in the subscription, including ones created later, so coverage becomes the default rather than a per-resource chore. It is also what carries the per-resource billing, so scope it deliberately.
4. Enforce it with policy, not the deprecated one
Assign the supported built-in policy that configures Defender for SQL on servers and Managed Instances so new subscriptions and resources inherit the plan. Do not assign the old 'Advanced Threat Protection types should be set to All' policy: it is deprecated. Configure the alert contact email and severity in Defender for Cloud so detections actually reach someone.
# 1. Enable Advanced Threat Protection on every logical server (detection only, no blocking).
for row in $(az sql server list --query "[].{n:name,g:resourceGroup}" -o tsv | tr '\t' ','); do
n=$(echo "$row" | cut -d',' -f1); g=$(echo "$row" | cut -d',' -f2)
az sql server advanced-threat-protection-setting update -g "$g" -n "$n" --state Enabled
echo "$n: advanced threat protection enabled"
done
# 2. Same for every SQL Managed Instance.
for row in $(az sql mi list --query "[].{n:name,g:resourceGroup}" -o tsv | tr '\t' ','); do
n=$(echo "$row" | cut -d',' -f1); g=$(echo "$row" | cut -d',' -f2)
az sql mi advanced-threat-protection-setting update -g "$g" -n "$n" --state Enabled
done
# 3. Turn on the Defender for Azure SQL plan for the whole subscription so new
# databases are covered automatically (this is what carries the per-resource cost).
az security pricing create -n SqlServers --tier Standard
# 4. Enforce at scale via the supported built-in policy (looked up by display name,
# never a hardcoded GUID). Do NOT assign the deprecated 'set to All' policy.
pid=$(az policy definition list \
--query "[?displayName=='Configure Azure Defender to be enabled on SQL servers and SQL Managed Instances'].name" -o tsv)
az policy assignment create \
--name enable-defender-sql \
--policy "$pid" \
--scope "/subscriptions/<subscription-id>" Quick quiz
Question 1 of 5Defender for Cloud shows 'All advanced threat protection types should be enabled' on several Azure SQL servers and a Managed Instance. What is the most efficient way to think about them?
You scored
0 / 5
Keep learning
Go deeper on what Advanced Threat Protection detects on Azure SQL and how the Defender for SQL plan delivers it across the estate.
- Advanced Threat Protection for Azure SQL Database The threat types Advanced Threat Protection detects and the alerts it raises.
- Configure Advanced Threat Protection for Azure SQL Database How to enable Advanced Threat Protection on a logical server and set alert contacts.
- Overview of Microsoft Defender for SQL How the Defender for Azure SQL plan enables detection and vulnerability assessment across a subscription, and how it is billed.
You can now treat Azure SQL threat detection as one capability rather than a scatter of findings: inventory every logical server and Managed Instance by detection state, enable Advanced Threat Protection on the highest-value databases first, turn on the Defender for Azure SQL plan so coverage is automatic for current and future databases, and enforce it with the supported policy rather than the deprecated one. The Controls this lesson covers section below links every recommendation in this group to its deep page and fix.
Back to the library