# Azure Dev/Test Storage Using GRS Replication

> ZopNight flags dev/test storage accounts running GRS, RAGRS, GZRS, or RAGZRS replication and prices the switch to LRS as the real per-account premium, (GRS rate − LRS rate) × stored GB. Geo-redundant storage costs roughly 2x the LRS rate. Without a positive measured premium the rule abstains entirely.

Source: https://zop.dev/integrations/azure/recommendations/azure-dev-test-storage-using-grs-replication
Updated: 2026-08-19

---

## Why geo-redundancy is wasted on dev/test data

Geo-redundant storage keeps six copies of every object: three in the primary region and three in an Azure-paired region, asynchronously replicated for regional-disaster survival. Dev/test data rarely deserves that. Build artifacts, test fixtures, and scratch datasets are reproducible by definition: if the region burns down, you regenerate them. Meanwhile the account pays roughly double the LRS rate for a durability guarantee nobody would ever invoke.

## Spot geo-redundant dev accounts

```bash
az storage account list \
  --query "[?contains(sku.name, 'GRS') || contains(sku.name, 'GZRS')].{name:name, sku:sku.name, rg:resourceGroup}" \
  -o table
```

Cross-reference the names against your environment conventions; ZopNight does the same match automatically.

## Four gates, all from real data

The rule fires only when every signal aligns. The account name must match a dev/test naming pattern, with infrastructure names such as "devops" explicitly excluded from the match. The discoverer-written `replication_type` metadata (the last underscore token of the storage SKU) must be GRS, RAGRS, GZRS, or RAGZRS; an absent key means the rule abstains rather than guessing. The aggregator must have supplied a positive monthly cost. And a positive geo-replication premium must have been measured for this specific account.

## How the dollar figure is computed

The saving is never a percentage of the bill. The aggregator computes the per-account premium as (GRS rate − base LRS rate) × stored GB, using a measured hot-GRS rate of $0.046 per GB-month from its pricing cache. When the GRS rate is missing from the cache or the stored size is unknown, no premium is emitted and the rule stays silent, a deliberate design that prefers reporting nothing over reporting a fabricated number. The premium is also clamped to the account's actual cost, so the projected optimized cost can never go negative.

## Downgrading and its trade-off

Flipping replication from GRS to LRS in the account's Configuration blade takes effect without downtime, but the move is a genuine durability reduction: a regional outage now means the data is unavailable until the region recovers, and a regional catastrophe means it is gone. For reproducible dev/test data that trade is usually correct. If an account graduates to production later, flip it back; the change is reversible in both directions.

Detection needs only the built-in Reader role via Azure Resource Graph; the cost inputs come through Cost Management Reader.
