Azure waste

Zone-redundant Elastic SAN outside production

An Elastic SAN provisioned Premium_ZRS while tagged non-production.
The agent reports this
Why it happens

Elastic SAN bills provisioned capacity, so it costs the same whether a byte is written or not, and the redundancy choice multiplies the whole bill. Zone-redundant storage is priced at exactly 1.5 times locally redundant on both of its meters: $0.12 against $0.08 per GiB a month for base units, which carry the SAN IOPS and throughput, and $0.09 against $0.06 for capacity units, which add space only. Zone redundancy buys survival of an availability zone failure, which a development or test SAN does not need. The remedy is not a setting: Microsoft states the redundancy option cannot be changed after creation, and moving off it means snapshotting the volumes, exporting them to managed disk snapshots, deploying a new SAN and rebuilding the volumes.

Paste this into Resource Graph Explorer in the Azure portal.

resources
| where type =~ 'microsoft.elasticsan/elasticsans'
// 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')
// Elastic SAN nests its sku inside properties, unlike most types, so the projected column is the
// fallback here rather than the primary. Reading only one of the two returns nothing.
| extend sanSku = tostring(coalesce(properties.sku.name, sku.name, sku.tier))
| where sanSku =~ 'Premium_ZRS'
// Sizes only, deliberately no dollar arithmetic. Baking the rates in here would go stale the day
// Azure reprices, which is the very thing the agent reads them live to avoid. See the note.
| extend baseTiB = toint(properties.baseSizeTiB), extraTiB = toint(properties.extendedCapacitySizeTiB)
| project name, resourceGroup, location, sku = sanSku, baseTiB, extraTiB, environment = cfopEnv

Elastic SAN bills provisioned capacity, so it costs the same whether a byte is written or not. Zone-redundant is exactly 1.5 times locally redundant on both of its meters, verified against the retail API: base units $0.12 against $0.08 per GiB a month, capacity units $0.09 against $0.06. Base units carry the SAN's IOPS and throughput; capacity units add space only, which is why pricing a SAN off a single meter is wrong. This query deliberately returns sizes rather than a dollar figure: rates baked into a published query go stale the day Azure reprices, which is exactly why the check reads all four live and converts TiB at 1024 GiB itself. Note the remedy is NOT a setting change: Microsoft states the redundancy option cannot be changed after creation, and moving off it means snapshotting the volumes, exporting them to managed disk snapshots, deploying a new SAN and rebuilding the volumes. This is a structural test, so what it returns is what the check reports.

Applies to microsoft.elasticsan/elasticsans
What the agent does about it

An Elastic SAN provisioned Premium_ZRS while 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.