Azure waste

Business Critical SQL in non-production

A test database on the top-tier plan
The agent reports this
Reversible
Why it happens

The top tier buys fast local storage and a spare copy for failover. A test database rarely needs either, and it costs roughly three times the standard tier.

Business Critical buys local SSD and a readable replica for high availability. A development database rarely needs either, and General Purpose costs substantially less.

Paste this into Resource Graph Explorer in the Azure portal.

resources
| where type in~ ('microsoft.sql/servers/databases', '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')
| where sku.tier in~ ('BusinessCritical','Premium')
| extend scope = iff(type =~ 'microsoft.sql/servers/elasticpools', 'pool', 'single')
| project name, resourceGroup, scope, env = cfopEnv,
          tier = tostring(sku.tier),
          sku = tostring(sku.name)

Elastic pools are listed alongside single databases because the check scans both: a pool carries its own tier and bills it whatever the databases inside are doing. Publishing databases only under-listed every Business Critical pool the agent would report. Anything here tagged non-production is worth questioning; check the availability requirement before moving tier.

Applies to microsoft.sql/servers/databases
What the agent does about it

A SQL database or elastic pool on Business Critical (or Premium DTU) tagged non-production.

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.