Azure waste

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.
The agent reports this
Reversible
Saving holds until someone undoes it
Why it happens

Serverless exists to stop charging when nobody is using the database, and auto-pause is the mechanism. Switched off, the database keeps its compute allocated and bills the minimum it was configured for every hour of the month, which is strictly worse than the provisioned tier it was chosen over. This rule does not invent an idleness threshold: it uses the two conditions Microsoft documents as what triggers a pause, no sessions and no user CPU, and fires only when both held at every sample across the window, which is a window in which Azure would have paused the database had the setting allowed it. Hyperscale is excluded because auto-pause is a General Purpose feature and its owner could not act on the finding, and databases on the free offer are excluded because they may cost nothing at all. The saving is the minimum compute floor and is reported as a lower bound, because Azure bills the greater of the minimum vCores and the minimum memory divided by three, and only the vCore half is readable from Resource Graph. Enabling auto-pause is not free of consequences: the first connection after a pause fails with error 40613 and must be retried, resume takes about a minute, and auto-pause is blocked entirely by geo-replication, long-term retention, a DNS alias on the server, or the database serving as a sync or elastic-job database, none of which this rule can see.

Paste this into Resource Graph Explorer in the Azure portal.

resources
| where type =~ 'microsoft.sql/servers/databases'
// 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 skuName = tostring(sku.name)
| extend tier = tostring(coalesce(properties.currentSku.tier, sku.tier))
| extend autoPauseDelay = properties.autoPauseDelay
// Serverless: the compute tier is named GP_S_ or HS_S_, or the database carries the
// autoPauseDelay setting at all, which only serverless databases do.
| where skuName startswith 'GP_S_' or skuName startswith 'HS_S_' or isnotnull(autoPauseDelay)
// Auto-pause is a General Purpose feature. A Hyperscale serverless database cannot pause,
// so its owner could not act on a finding and it is excluded rather than listed.
| where tolower(tier) contains 'generalpurpose' or skuName startswith 'GP_S_'
// The free offer may cost nothing at all, so a database on it is not waste.
| where tobool(coalesce(properties.useFreeLimit, false)) == false
// -1 is what Microsoft documents as "automatic pause is disabled". This is the setting
// the whole check is about: anything else means auto-pause is already on.
| where toint(autoPauseDelay) == -1
| project name, resourceGroup, location, skuName, tier,
          minCapacityVCores = todouble(properties.minCapacity),
          autoPauseDelay = toint(autoPauseDelay)
| order by name asc

Each row is a General Purpose serverless SQL database with auto-pause switched OFF. That setting is the precondition for the whole waste and it is a one-property change, so the list is worth acting on by itself: serverless exists to stop charging when nobody is using the database, and with auto-pause off it keeps its compute allocated and bills the minimum it was configured for every hour of the month, which is strictly worse than the provisioned tier it was chosen over. Resource Graph holds no metrics, so this cannot apply the part that decides. The agent additionally requires Azure's own pause condition to have held at EVERY sample across the window - no sessions and no user CPU - which is a window in which Azure would have paused the database had the setting allowed it. So a row here is a candidate, not a finding. Three exclusions the agent makes are already applied above and are deliberate: Hyperscale serverless databases are left out because auto-pause is a General Purpose feature and their owner could not act on the finding, databases on the free offer are left out because they may cost nothing at all, and databases whose autoPauseDelay is anything other than -1 already have auto-pause enabled. Enabling auto-pause is not free of consequences and the agent says so too: the first connection after a pause fails with error 40613 and must be retried, resume takes about a minute, and auto-pause is blocked entirely by geo-replication, long-term retention, a DNS alias on the server, or the database serving as a sync or elastic-job database, none of which this query or the agent can see.

Applies to microsoft.sql/servers/databases
What the agent does about 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.

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.