Microsoft Defender for Cloud · Azure Database for MySQL
Public network access should be disabled for MySQL servers
Written and reviewed by Emnode · Last reviewed
What does the recommendation "Public network access should be disabled for MySQL servers" check?
Flags any Azure Database for MySQL Single Server (resource type Microsoft.DBforMySQL/servers) whose 'publicNetworkAccess' property is set to 'Enabled', meaning the server exposes a public endpoint that clients can reach across the internet subject only to its firewall rules. When the property is 'Disabled', connectivity is restricted to private endpoints and the public listener is switched off entirely. This control evaluates only the public-access toggle on Single Server. It does not look at the contents of your firewall allowlist, at SSL enforcement, or at Flexible Server (Microsoft.DBforMySQL/flexibleServers), which is governed by a separate recommendation. Single Server is on Microsoft's retirement path, so a flag here is also a prompt to plan migration to Flexible Server.
Why does "Public network access should be disabled for MySQL servers" matter?
A MySQL server with public network access enabled is reachable from any network unless every firewall rule is perfectly scoped, and a single permissive rule such as 0.0.0.0 to 255.255.255.255, or the 'Allow access to Azure services' option, leaves the database open to credential-stuffing and brute-force attempts from the open internet. Databases hold the data attackers most want: customer records, credentials and financial detail. One exposed instance can turn a stolen or weak password into a full breach, with the regulatory fines, breach notification and customer loss that follow. Disabling public access removes the internet-facing attack surface completely, so the server can only be reached through a private endpoint inside your virtual network and a leaked password alone is no longer enough to connect.
How do I fix "Public network access should be disabled for MySQL servers"?
- On the MySQL Single Server, set 'Deny public network access' to 'Yes' under Connection security, or run 'az mysql server update --resource-group <rg> --name <server> --public-network-access Disabled'. Provision an Azure Private Endpoint first so authorised clients keep a path in.
- Express the setting in Bicep as the 'publicNetworkAccess: 'Disabled'' property on the Microsoft.DBforMySQL/servers resource, so redeployments cannot silently re-enable the public endpoint.
- Enforce it across the subscription by assigning the built-in policy by display name rather than a hardcoded GUID: pid=$(az policy definition list --query "[?displayName=='Public network access should be disabled for MySQL servers'].name" -o tsv); az policy assignment create --name deny-mysql-public --policy "$pid" --scope <scope>. Use the Deny effect to block new servers being created with public access on.
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 "Public network access should be disabled for MySQL servers" a false positive?
A short-lived Single Server kept public on purpose during a migration to Flexible Server, while data is copied from on-premises or third-party tooling that cannot yet reach a private endpoint, is a legitimate exception. Restrict it to specific source IPs, set a removal date, and disable public access or retire the Single Server as soon as the cutover completes.
More Azure Database for MySQL controls
- (Enable if required) MySQL servers should use customer-managed keys to encrypt data at rest
- Azure Database for MySQL should have a Microsoft Entra administrator provisioned
- Enforce SSL connection should be enabled for MySQL database servers
- Geo-redundant backup should be enabled for Azure Database for MySQL
- Private endpoint should be enabled for MySQL servers