Skip to main content
emnode
Compliance Medium severity MCSB LT-3

Microsoft Defender for Cloud · Azure Database for PostgreSQL flexible server

pgaudit.log_level should be set to "log" for Azure Database for PostgreSQL Servers

Written and reviewed by Emnode · Last reviewed

What does the recommendation "pgaudit.log_level should be set to "log" for Azure Database for PostgreSQL Servers" check?

Inspects the 'pgaudit.log_level' server parameter on each Azure Database for PostgreSQL flexible server and flags any server where it is not set to 'log'. This parameter controls the PostgreSQL severity level at which the pgaudit extension emits its audit entries, and the control wants those entries written at 'log' level so they are reliably captured and routed by the platform. It does not check that pgaudit is in the 'azure.extensions' allowlist, that 'pgaudit.log' selects any statement classes, or that 'pgaudit.log_client' is on. Because 'pgaudit.log_level' only takes effect when 'pgaudit.log_client' is enabled, a passing value here does not on its own prove that audit records are actually being produced or shipped to a Log Analytics workspace.

Why does "pgaudit.log_level should be set to "log" for Azure Database for PostgreSQL Servers" matter?

Audit records are only useful if they survive long enough to be read. If pgaudit emits at a level such as 'debug1' or 'info', entries can be dropped below the server's 'log_min_messages' threshold or scattered into verbose diagnostic noise, so the trail of who read or changed sensitive rows quietly disappears. During an incident or a regulator's request you then cannot answer who touched a given table, which turns a contained event into a reportable breach and undermines defence of any compliance claim. Pinning the level to 'log' keeps audit output at the standard severity the platform forwards, so the security history you depend on is consistently retained.

How do I fix "pgaudit.log_level should be set to "log" for Azure Database for PostgreSQL Servers"?

  1. Set the 'pgaudit.log_level' parameter to 'log' on the server: az postgres flexible-server parameter set --resource-group <rg> --server-name <server> --source user-override --name pgaudit.log_level --value log
  2. Make the setting actually emit records by enabling the audit pipeline it depends on: add 'pgaudit' to the 'azure.extensions' allowlist, turn on 'pgaudit.log_client', and select statement classes with 'pgaudit.log' (for example READ,WRITE) using the same 'az postgres flexible-server parameter set' command for each parameter.
  3. Codify the value in your infrastructure modules so new servers inherit it: in Bicep, declare a 'configurations' child resource named 'pgaudit.log_level' under 'Microsoft.DBforPostgreSQL/flexibleServers' with properties value 'log' and source 'user-override', then route the diagnostic 'PostgreSQLLogs' category to a Log Analytics workspace.

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 "pgaudit.log_level should be set to "log" for Azure Database for PostgreSQL Servers" a false positive?

A server where someone has intentionally relabelled audit output, for example pgaudit.log_level set to 'warning' or 'notice', will still flag because the control checks for the exact value 'log'. Note that this parameter only changes the severity label stamped on audit entries (it is documented as primarily for regression testing) and does not widen which events are audited, so the same records are simply emitted under a different level. If a non-standard level was set deliberately, restore it to 'log' so entries are forwarded at the severity the platform expects, or accept the finding with a documented justification while the other level is in force.