Azure waste

Analysis Services server running with no queries and no processing

A running Azure Analysis Services server with no user sessions, no query threads and no model processing in the window, billing its hourly rate for every replica.
The agent reports this
Reversible
Why it happens

An Analysis Services server bills by the hour for as long as it is running. Every retail price record for the service, across all regions and SKUs, is an hourly capacity meter and there is no per-query meter at all, so the bill is a function of uptime, SKU and replica count rather than of use. Microsoft lists pausing among the levers that change the cost. Idleness here has to mean two things at once, and this rule requires both: no interactive use, measured by user sessions and busy query threads, and no model processing, measured by the rate of rows read and by the processing pool own busy threads. That second half matters because a server can serve no query for a fortnight and still be earning its keep by refreshing a model every night, and Azure instruments refresh separately from querying; a rule reading only the query side would recommend pausing the server that rebuilds the warehouse each night. Every figure is read at its maximum over the window, so a single busy sample anywhere disqualifies it, and a metric that returned no data at all is treated as unknown rather than as zero. Memory is deliberately ignored because it reflects the model loaded into the server rather than anything anybody is doing, the idle-thread counters are ignored because they read high precisely when the server is doing nothing, and the memory limit counters are ignored because Azure documents them as coming from the configuration file, which makes them the SKU ceiling rather than a measurement. The cost is the hourly rate for the SKU multiplied by the replica count, because a query replica bills at the same rate as the server it copies. Pausing is reversible from the portal or PowerShell, and what it costs to resume is not something Azure documents, so the figure is the server current run rate rather than a promise about the next bill.

Paste this into Resource Graph Explorer in the Azure portal.

resources
| where type =~ 'microsoft.analysisservices/servers'
// 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 tostring(properties.state) == 'Succeeded'
| project server = name, resourceGroup, location,
          sku = tostring(sku.name),
          instances = toint(sku.capacity)

properties.state is the running state; a paused server reads Paused or Suspended. Resource Graph cannot show whether anyone used it, so confirm with the Azure Monitor metrics CurrentUserSessions and QueryPoolBusyThreads for queries, and RowsReadPerSec and ProcessingPoolBusyIOJobThreads for model refreshes, before pausing anything.

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

A running Azure Analysis Services server with no user sessions, no query threads and no model processing in the window, billing its hourly rate for every replica.

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.