SQL elastic pools with no databases
A shared database pool with no databases in it
Why it happens
The pool reserves and bills capacity up front. Databases get moved out during a tidy-up and the empty pool keeps charging.
An elastic pool bills for its reserved capacity whether or not any database sits in it. Databases get moved out during a consolidation and the pool is left.
Paste this into Resource Graph Explorer in the Azure portal.
resources
| where type =~ 'microsoft.sql/servers/elasticpools'
// Scope below matches CloudFinOpsPro policy DEFAULTS: exclusions.protectTag, environment.tagKeys,
// environment.allowedTagValues, and allowUntagged false so an untagged resource counts as production.
// If your policy.yaml differs, edit the three lines below or this will disagree with your own agent.
| where tolower(tostring(coalesce(tags['cfop:protect'], ''))) !in ('true', 'yes', '1')
| extend cfopEnv = tolower(trim(' ', tostring(coalesce(tags['Environment'], tags['environment'], tags['env'], ''))))
| where cfopEnv in ('dev', 'development', 'test', 'qa', 'sandbox', 'nonprod', 'non-prod', 'staging')
| extend poolId = tolower(id)
| join kind=leftouter (
resources
| where type =~ 'microsoft.sql/servers/databases'
| extend poolId = tolower(tostring(properties.elasticPoolId))
| where isnotempty(poolId)
| summarize databases = count() by poolId
) on poolId
| extend databases = coalesce(databases, 0)
| where databases <= 0
| project pool = name, resourceGroup, location, databases,
sku = tostring(sku.name),
capacity = toint(sku.capacity)The rule is named for pools with NO databases and the previous version of this query counted none, listing every pool you own including busy ones. The join is the whole check: a database names its pool in properties.elasticPoolId, and the detector lowercases both sides because that property returns the ARM id in either case. Deleting a pool that still hosts databases takes them offline, which is why the detector refuses to report anything at all when databases were not collected rather than assuming a pool is empty, and why a query without the count is the wrong shape rather than merely a loose one. Honest about verification: the reference subscription owns no elastic pools, so this returns nothing there and the selection is unproven on real pools. What was proven is the mechanism, by running the identical join and summarize against virtual machines and their disks in the same subscription, which returned the three machines with one disk each.
Applies to microsoft.sql/servers/elasticpoolsWhat the agent does about it
A SQL elastic pool billing its configured DTU or vCore capacity with no databases inside it.
It will not fix this one
It reports this and takes no action. A check only earns the right to change something once the action behind it can re-read live state before it fires and knows the way back.
Related checks
HDInsight worker nodes without autoscale
A Spark, Hadoop or Interactive Query cluster whose worker role has no autoscale configured, so it pays for peak worker capacity every hour of every day.
SQL Managed Instance without Azure Hybrid Benefit
A SQL Managed Instance paying the licence-included rate, which a customer holding SQL Server licences with Software Assurance can lower without downtime.
Serverless SQL database with auto-pause switched off and nothing using it
A General Purpose serverless SQL database with auto-pause disabled that had no sessions and no CPU at any point in the window, so it billed its minimum compute floor around the clock for doing nothing.