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

Microsoft Defender for Cloud · Azure Event Grid

Azure Event Grid topics should use private link

Written and reviewed by Emnode · Last reviewed

What does the recommendation "Azure Event Grid topics should use private link" check?

Flags any Event Grid custom topic that has no approved private endpoint connection, meaning publishers reach the topic over a public endpoint rather than a private IP inside your virtual network. The check looks at the topic's private endpoint connection state, not at the IP firewall rules or the 'Public network access' toggle, so a topic with a restrictive firewall but no private endpoint still flags. It also does not cover Event Grid domains, system topics or namespace topics, which are governed by their own separate recommendations.

Why does "Azure Event Grid topics should use private link" matter?

Without private link, the topic's publishing endpoint resolves to a public address and is reachable from anywhere on the internet that holds a key or token. A leaked SAS key or shared access signature then lets an attacker inject forged events from outside your network, and those events can trigger downstream functions, logic apps and webhooks, turning one credential into arbitrary action across your event-driven workloads. Routing publishers through a private endpoint keeps event traffic on the Azure backbone, so a stolen key alone is not enough to reach the topic from the open internet. It also minimises the exposed attack surface that automated scanners probe and gives your network team a single private path they can monitor and reason about.

How do I fix "Azure Event Grid topics should use private link"?

  1. Create a private endpoint for the topic so publishers connect over a private IP: az network private-endpoint create --resource-group <rg> --name <pe-name> --vnet-name <vnet> --subnet <subnet> --private-connection-resource-id <topic-resource-id> --group-ids topic --connection-name <conn-name>.
  2. Once the private endpoint is approved and clients use the private DNS zone, disable the public path so only private traffic is accepted: az eventgrid topic update --resource-group <rg> --name <topic> --public-network-access disabled.
  3. Enforce the pattern going forward by assigning the built-in audit policy, looking the definition up by name rather than hardcoding its id: pid=$(az policy definition list --query "[?displayName=='Azure Event Grid topics should use private link'].name" -o tsv); az policy assignment create --name evgrid-private-link --policy "$pid" --scope <scope>.

Remediation script · bash

# 1. Stand up a private endpoint for a storage account's blob sub-resource,
#    then wire private DNS so the existing hostname resolves to the private IP.
SA_ID=$(az storage account show -g rg-data -n custexportsprod --query id -o tsv)

az network private-endpoint create \
  --name pe-blob-prod \
  --resource-group rg-data \
  --vnet-name vnet-app \
  --subnet snet-privatelink \
  --private-connection-resource-id "$SA_ID" \
  --group-id blob \
  --connection-name pe-blob-prod-conn

# Link the matching private DNS zone and register the A record automatically.
az network private-dns zone create -g rg-data -n privatelink.blob.core.windows.net
az network private-dns link vnet create -g rg-data \
  --zone-name privatelink.blob.core.windows.net \
  --name link-vnet-app --virtual-network vnet-app --registration-enabled false
az network private-endpoint dns-zone-group create \
  --resource-group rg-data --endpoint-name pe-blob-prod \
  --name zg-blob --private-dns-zone privatelink.blob.core.windows.net --zone-name blob

# 2. Only after verifying the app resolves and connects privately, close the public door.
az storage account update -g rg-data -n custexportsprod --public-network-access Disabled

# 3. Ratchet it shut: assign the AUDIT private-link policy by its exact display name,
#    looking up the definition id at runtime (never paste a hardcoded GUID).
pid=$(az policy definition list \
  --query "[?displayName=='Storage account should use a private link connection'].name" -o tsv)
az policy assignment create \
  --name audit-storage-private-link \
  --policy "$pid" \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000"

Full walkthrough (console steps, edge cases and verification) in the lesson Require private endpoints for Azure PaaS services.

Is "Azure Event Grid topics should use private link" a false positive?

A topic that only ever receives events from another Azure service over a private network, but was provisioned before private endpoints were added, can still report as compliant once a private endpoint exists even if 'Public network access' is left enabled, because the recommendation keys on the private link connection rather than the public toggle. Conversely, a deliberately public ingestion topic that must accept events from external partners over the internet will keep flagging by design: in that case front it with IP firewall rules and key rotation and record a documented exception rather than forcing a private endpoint it cannot use.