Back to blog
AzureBest PracticesCloud SecurityDatabasesIdentity & Access

Fixing Azure PostgreSQL Servers With No Azure AD Admin

Learn why an Azure PostgreSQL server without an Entra ID administrator is a security risk and how to fix it with CLI, Terraform, and Azure Policy.

TL;DR

This check flags Azure PostgreSQL servers that rely only on native database logins with no Azure AD administrator. Without Entra ID integration you lose centralized identity, MFA, and instant offboarding. Fix it by assigning an Azure AD admin to the server.

Database access is one of the easiest places for credentials to rot. Someone spins up a PostgreSQL server, sets a password for the admin login, drops it into a config file or a secrets manager, and moves on. Six months later that password is still valid, shared across three services, and nobody remembers who has it. The PostgreSQL Has No Azure AD Admin check catches the root of that problem: a PostgreSQL server that has no Azure Active Directory (now Microsoft Entra ID) administrator configured.

This applies to Azure Database for PostgreSQL, both the Single Server and Flexible Server deployment models. When no Entra ID admin is set, the only way in is through native PostgreSQL authentication, which means standalone usernames and passwords that live outside your identity provider.


What this check detects

The check inspects each Azure PostgreSQL server in your subscription and verifies whether an Azure AD administrator is assigned. If the administrator property is empty, the server is flagged.

An Azure AD admin is a user, group, or service principal from your Entra ID tenant that is designated as a database administrator. Once configured, that identity (and any other Entra principals you grant access to) can authenticate to PostgreSQL using tokens issued by Entra ID instead of a password stored in the database itself.

Note: Configuring an Azure AD admin does not disable native PostgreSQL authentication by default. The two can coexist. Setting the admin is the prerequisite for using Entra-based logins, and on Flexible Server you can optionally turn off password authentication entirely once Entra is in place.


Why it matters

Native database passwords are a category of secret that traditional identity controls never see. When you depend on them exclusively, several real problems follow.

No centralized offboarding

When an engineer leaves the company, disabling their Entra ID account should cut off their access everywhere. But a native PostgreSQL login is invisible to that process. The departing employee may still hold a valid psql connection string. You are now relying on someone remembering to manually rotate that database password, which rarely happens cleanly.

No MFA, no conditional access

Entra ID lets you enforce multi-factor authentication, block logins from risky locations, and require compliant devices. None of that protection reaches a database that only accepts a username and password. A leaked connection string is a direct, unconditional path into your data.

Password sprawl and reuse

Native admin passwords tend to get embedded in application configs, CI pipelines, and shared documents. Each copy is a place the secret can leak. Token-based Entra authentication avoids storing a long-lived password at all, since the application requests a short-lived token at connection time.

Weaker audit trail

When access flows through Entra ID, sign-in logs tie database connections back to real identities and you get a consistent audit story across your estate. Native logins give you far less context about who actually connected and from where.

Warning: A missing Azure AD admin is often a sign that the server was provisioned quickly and never hardened. Treat this flag as a prompt to also review firewall rules, SSL enforcement, and password complexity on the same server.


How to fix it

The remediation is to assign an Azure AD administrator to the server. Use a security group rather than an individual user wherever possible, so you can manage membership without touching the database configuration again.

Option 1: Azure CLI (Flexible Server)

First grab the object ID of the user or group you want to make admin.

# Look up the object ID of a group named "pg-admins"
az ad group show --group "pg-admins" --query id -o tsv

Then assign it as the Entra ID administrator on the server.

az postgres flexible-server ad-admin create \
  --resource-group my-rg \
  --server-name my-pg-server \
  --display-name "pg-admins" \
  --object-id "00000000-0000-0000-0000-000000000000" \
  --type Group

Option 2: Azure CLI (Single Server)

The Single Server model uses a slightly different command path.

az postgres server ad-admin create \
  --resource-group my-rg \
  --server-name my-pg-server \
  --display-name "pg-admins" \
  --object-id "00000000-0000-0000-0000-000000000000"

Note: Microsoft is retiring the Single Server deployment option. If you are still on Single Server, plan a migration to Flexible Server, which has better Entra ID support including the ability to add multiple Entra administrators.

Option 3: Azure Portal

  1. Open your PostgreSQL server in the Azure Portal.
  2. Under Security (Flexible Server) or Settings (Single Server), select Authentication or Active Directory admin.
  3. Choose Set admin, search for your group or user, and select it.
  4. Click Save.

Option 4: Terraform

For Flexible Server, define the admin as a dedicated resource so it lives in version control alongside the server.

resource "azurerm_postgresql_flexible_server" "main" {
  name                = "my-pg-server"
  resource_group_name = azurerm_resource_group.main.name
  location            = azurerm_resource_group.main.location
  version             = "16"
  sku_name            = "GP_Standard_D2s_v3"
  storage_mb          = 32768

  authentication {
    active_directory_auth_enabled = true
    password_auth_enabled         = false
  }
}

resource "azurerm_postgresql_flexible_server_active_directory_administrator" "admin" {
  server_name         = azurerm_postgresql_flexible_server.main.name
  resource_group_name = azurerm_resource_group.main.name
  tenant_id           = data.azurerm_client_config.current.tenant_id
  object_id           = azuread_group.pg_admins.object_id
  principal_name      = azuread_group.pg_admins.display_name
  principal_type      = "Group"
}

Danger: Setting password_auth_enabled = false immediately breaks any application or user still authenticating with a native password. Before you disable password auth, confirm every client can obtain and use Entra tokens, or you will lock out production. Roll this out in a non-production environment first.

Connecting with an Entra token

Once the admin is set, an authorized principal connects by requesting a token and passing it as the password.

# Request an access token for the PostgreSQL resource
export PGPASSWORD=$(az account get-access-token \
  --resource-type oss-rdbms \
  --query accessToken -o tsv)

psql "host=my-pg-server.postgres.database.azure.com \
  port=5432 dbname=postgres \
  user=pg-admins sslmode=require"

Tip: For application workloads, skip stored credentials entirely by using a managed identity. Assign the identity to your App Service, Container App, or VM, grant it access in PostgreSQL, and have the app fetch tokens via the metadata endpoint. No password ever touches your code or config.


How to prevent it from happening again

Fixing one server is easy. Keeping every future server compliant is the real win. Push the control left so a misconfigured server never reaches production.

Azure Policy

Azure ships a built-in policy definition that audits PostgreSQL servers without an Entra ID admin. Assign it at the subscription or management group scope so it covers everything by default.

az policy assignment create \
  --name "require-pg-aad-admin" \
  --display-name "PostgreSQL servers must have an Azure AD admin" \
  --policy "/providers/Microsoft.Authorization/policyDefinitions/b4dec045-250a-48c2-b5cc-e0c4eec8b5b4" \
  --scope "/subscriptions/"

Start the policy in Audit effect to map your current exposure, then graduate it to Deny once your pipelines reliably set an admin.

CI/CD gates

If you provision with Terraform, fail the build when a PostgreSQL resource lacks an Entra admin. A Checkov or tfsec scan in your pipeline catches it before apply:

checkov -d ./infra --framework terraform

You can also write a custom OPA/Conftest rule that rejects any plan containing an azurerm_postgresql_flexible_server without a matching administrator resource.

Continuous monitoring with Lensix

Policies and pipeline scans cover the resources you create through approved paths. Lensix runs the postgresql_noadmin check continuously across your subscriptions, so a server created manually in the portal, or one that drifts after a change, surfaces in your findings without anyone having to remember to look.


Best practices

  • Assign groups, not people. Make a security group the Entra admin, then manage who has access through group membership. The database config stays untouched as your team changes.
  • Prefer managed identities for apps. Workloads should authenticate with a system or user-assigned managed identity rather than any stored credential.
  • Disable password auth once you are ready. On Flexible Server, turning off native passwords closes the last path that bypasses your identity controls. Validate every client first.
  • Enforce SSL and least privilege. Setting an Entra admin is one layer. Pair it with require_secure_transport on, tight firewall rules, and database roles scoped to exactly what each principal needs.
  • Send sign-in logs somewhere useful. Route Entra sign-in and PostgreSQL logs to Log Analytics so you can actually investigate who connected when something looks off.

Configuring an Azure AD admin is a small change that unlocks the whole Entra ID security stack for your databases: MFA, conditional access, central offboarding, and token-based auth that keeps long-lived passwords out of your stack. It is one of the highest-value, lowest-effort hardening steps available for Azure PostgreSQL.