Skip to main content
emnode
Compliance Low severity MCSB BR-1

Microsoft Defender for Cloud · Azure Database for MySQL

Geo-redundant backup should be enabled for Azure Database for MySQL

Written and reviewed by Emnode · Last reviewed

What does the recommendation "Geo-redundant backup should be enabled for Azure Database for MySQL" check?

Audits Azure Database for MySQL Single Server instances (Microsoft.DBforMySQL/servers) where storageProfile.geoRedundantBackup is not set to Enabled, meaning automated backups are written only to locally redundant storage in the server's own region. It is an audit-only check on the backup storage redundancy property, so it does not validate that backups are actually succeeding, does not test your ability to restore, and does not measure backup retention. It also does not cover the newer Flexible Server type (Microsoft.DBforMySQL/flexibleServers); note that the Single Server deployment model this recommendation targets reached retirement on 16 September 2024, so the underlying check is effectively legacy.

Why does "Geo-redundant backup should be enabled for Azure Database for MySQL" matter?

With locally redundant backups, every copy of your data lives in a single region. If that region suffers a prolonged outage or a backup-storage failure, you cannot restore the database anywhere until the region recovers, and your recovery point objective is bounded by an event entirely outside your control. Geo-redundant backup replicates each backup to the Azure paired region, so a regional disaster becomes a geo-restore into the paired region rather than an open-ended outage. For a customer-facing or revenue-bearing database, that is the difference between hours of degraded service and a multi-day data-availability incident with the contractual and reputational cost that follows.

How do I fix "Geo-redundant backup should be enabled for Azure Database for MySQL"?

  1. Recognise that backup storage redundancy can only be chosen when the server is created and cannot be changed afterwards, so existing non-compliant servers must be re-provisioned rather than patched in place.
  2. Create the replacement on Flexible Server (the supported successor) with geo-redundancy on, for example 'az mysql flexible-server create --resource-group <rg> --name <server> --geo-redundant-backup Enabled', then migrate the data; on a legacy Single Server the equivalent create-time flag is 'az mysql server create ... --geo-redundant-backup Enabled' on a General Purpose or Memory Optimized tier, since the Basic tier does not support it.
  3. Assign the audit policy by its display name rather than a hardcoded id so the safe default is enforced going forward: pid=$(az policy definition list --query "[?displayName=='Geo-redundant backup should be enabled for Azure Database for MySQL'].name" -o tsv); az policy assignment create --name mysql-geo-backup --policy "$pid" --scope <scope>.

Remediation script · bash

# 1. Lock the wire and the network on a production PostgreSQL flexible server.
RG=db-prod-rg
SRV=ordersprod-pg

# Enforce encrypted connections (dynamic, no restart needed).
az postgres flexible-server parameter set \
  --resource-group "$RG" --server-name "$SRV" \
  --source user-override \
  --name require_secure_transport --value ON

# Pin the minimum TLS version.
az postgres flexible-server parameter set \
  --resource-group "$RG" --server-name "$SRV" \
  --source user-override \
  --name ssl_min_protocol_version --value TLSv1.2

# Disable public network access.
az postgres flexible-server update \
  --resource-group "$RG" --name "$SRV" \
  --public-access Disabled

# 2. Turn on the pgaudit trail and connection throttling.
for p in "pgaudit.log=role,ddl,misc" \
         "pgaudit.log_client=ON" \
         "pgaudit.log_level=log" \
         "pgaudit.log_statement=on" \
         "pgaudit.log_statement_once=on" \
         "connection_throttle.enable=on"; do
  az postgres flexible-server parameter set \
    --resource-group "$RG" --server-name "$SRV" \
    --source user-override \
    --name "${p%%=*}" --value "${p#*=}"
done

# 3. Provision a Microsoft Entra administrator and disable password sign-in.
az postgres flexible-server ad-admin create \
  --resource-group "$RG" --server-name "$SRV" \
  --display-name "dba-team" \
  --object-id "$(az ad group show --group dba-team --query id -o tsv)" \
  --type Group
az postgres flexible-server update \
  --resource-group "$RG" --name "$SRV" \
  --microsoft-entra-auth Enabled --password-auth Disabled

# 4. Ratchet it shut: deny any future PostgreSQL flexible server with public access.
# Look the built-in policy up by its EXACT display name, never paste a guessed GUID.
PID=$(az policy definition list \
  --query "[?displayName=='Public network access should be disabled for PostgreSQL flexible servers'].name" \
  -o tsv)
az policy assignment create \
  --name deny-public-pg-flex \
  --policy "$PID" \
  --params '{"effect":{"value":"Deny"}}' \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000"

Full walkthrough (console steps, edge cases and verification) in the lesson Harden Azure MySQL and PostgreSQL.

Is "Geo-redundant backup should be enabled for Azure Database for MySQL" a false positive?

A short-lived development or test server, or a read-only analytics copy that can simply be rebuilt from the production source, is correctly excluded: paying for cross-region backup replication on data that has no independent recovery value adds cost without reducing real risk. Exempt such servers from the assignment with a documented az policy exemption scoped to those resources, rather than disabling the policy for the whole subscription.