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

Microsoft Defender for Cloud · Azure Database for PostgreSQL

Private endpoint should be enabled for PostgreSQL servers

Written and reviewed by Emnode · Last reviewed

What does the recommendation "Private endpoint should be enabled for PostgreSQL servers" check?

Audits each Azure Database for PostgreSQL Single Server (resource type Microsoft.DBforPostgreSQL/servers) and flags any that has no approved private endpoint connection. The check confirms a Private Link connection exists so traffic reaches the server over a private IP inside your virtual network. It does not verify that public network access is switched off, that firewall rules are tight, or that private DNS resolves correctly, those are separate controls. It also does not apply to Flexible Server, which is governed by its own recommendation, and Single Server is on Microsoft's retirement path, so treat a flag here as a prompt to migrate where you can rather than only to bolt on a private endpoint.

Why does "Private endpoint should be enabled for PostgreSQL servers" matter?

Without a private endpoint, connections to the PostgreSQL server traverse a public endpoint, and access is gated only by firewall rules that are easy to widen by accident, for example a 0.0.0.0 'allow Azure services' rule that quietly opens the server to every workload in the platform. That leaves credentials and query traffic exposed to the public internet and to lateral movement from other Azure tenants, and a single over-broad rule can undo months of careful network design. A private endpoint pins the server to a known subnet so only your network can reach it, which shrinks the attack surface and is frequently a hard requirement for handling regulated or personal data under frameworks such as PCI DSS and ISO 27001. The business consequence of skipping it is a data-exfiltration path that auditors and attackers both look for first, and a failed control here often blocks a compliance sign-off.

How do I fix "Private endpoint should be enabled for PostgreSQL servers"?

  1. Create the private endpoint against the server's resource id, naming the PostgreSQL subresource group: pe_id=$(az postgres server show -g <rg> -n <server> --query id -o tsv); az network private-endpoint create -g <rg> -n <server>-pe --vnet-name <vnet> --subnet <subnet> --private-connection-resource-id "$pe_id" --group-id postgresqlServer --connection-name <server>-conn.
  2. Link a privatelink.postgres.database.azure.com private DNS zone to the virtual network and add a record set for the endpoint so the server FQDN resolves to the private IP, otherwise clients keep using the public address.
  3. Roll this out at scale by assigning the built-in audit policy. Look it up by display name rather than pasting a GUID: pid=$(az policy definition list --query "[?displayName=='Private endpoint should be enabled for PostgreSQL servers'].name" -o tsv); az policy assignment create --name pg-private-endpoint --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 "Private endpoint should be enabled for PostgreSQL servers" a false positive?

A non-production or sandbox Single Server that holds only synthetic test data and sits behind disabled public network access can be a deliberate exception, since the private-endpoint requirement exists to protect real data in transit. Document the exception and prefer migrating it to Flexible Server rather than leaving the flag standing indefinitely.