Skip to main content
emnode
Compliance Medium severity MCSB NS-2

Microsoft Defender for Cloud · Azure Database for PostgreSQL flexible server

Public IP access should be disabled for Azure Database for PostgreSQL Servers

Written and reviewed by Emnode · Last reviewed

What does the recommendation "Public IP access should be disabled for Azure Database for PostgreSQL Servers" check?

Flags any Azure Database for PostgreSQL flexible server whose 'network.publicNetworkAccess' property is still Enabled, meaning the server is reachable over a public endpoint and firewall rules decide which source IP ranges can connect. The control wants this set to Disabled so connectivity is only possible through a private endpoint inside your virtual network. It evaluates the account-level networking mode, not the contents of individual firewall rules: a server with public access enabled flags even if every firewall rule is narrow. It also does not cover the legacy single-server deployment, in-database role and password hygiene, or whether TLS is enforced. Those are separate recommendations.

Why does "Public IP access should be disabled for Azure Database for PostgreSQL Servers" matter?

A PostgreSQL server with public access enabled is exposed to the internet, with only firewall rules between an attacker and a login prompt. The common failure is the 'Allow public access from any Azure service' option or a 0.0.0.0 to 255.255.255.255 rule added during testing and never removed, which leaves the database open to the entire Azure backbone or the open internet. From there an attacker can brute-force credentials, exploit a leaked connection string, or pivot from a compromised workload. Databases hold the data that actually matters: customer records, financial data, application secrets, so an exposed server turns a minor credential leak into a full breach with notification and regulatory consequences. Disabling public access removes the public endpoint entirely, so a stray firewall rule can no longer expose the data.

How do I fix "Public IP access should be disabled for Azure Database for PostgreSQL Servers"?

  1. Confirm a private path exists first: provision a private endpoint (or use VNet integration) so your application can still reach the server, because disabling public access drops all enforcement of existing firewall rules and breaks any client that depended on them.
  2. Disable the public endpoint on the server with 'az postgres flexible-server update --resource-group <rg> --name <server> --public-access Disabled', or set 'properties.network.publicNetworkAccess' to 'Disabled' in your Bicep or ARM template so the safe state is declared in code.
  3. Enforce it at scale by assigning the built-in policy 'Public network access should be disabled for PostgreSQL flexible servers' with a Deny effect. Look the definition up by display name rather than pasting a 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-pg-public --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 "Public IP access should be disabled for Azure Database for PostgreSQL Servers" a false positive?

A development or sandbox server that intentionally uses public access with a tightly scoped firewall rule, for example a single office egress IP, still flags because the control measures whether the public endpoint exists rather than how narrow the rule is. If that exposure is a deliberate, reviewed exception for a non-production server holding no real data, exempt that specific resource from the policy and record the justification, rather than leaving the recommendation silently unaddressed across the estate.