Microsoft Defender for Cloud · Azure SignalR Service
Azure SignalR Service should use private link
Written and reviewed by Emnode · Last reviewed
What does the recommendation "Azure SignalR Service should use private link" check?
Audits each Azure SignalR Service resource for at least one approved private endpoint connection, so clients reach it over a private IP on your virtual network instead of its public hostname. It inspects the resource's privateEndpointConnections collection and reads the connection's privateLinkServiceConnectionState.status, treating a resource with no approved connection as non-compliant. It does not check whether public network access is switched off, nor does it evaluate the IP firewall rules, the service mode or upstream settings, so a SignalR instance can satisfy this control while still being reachable on its public endpoint. It is an audit-only policy mapped to Azure Security Benchmark NS-2: it reports non-compliant resources in Defender for Cloud, it never blocks creation or remediates automatically.
Why does "Azure SignalR Service should use private link" matter?
Without a private endpoint, SignalR negotiation and the persistent WebSocket carrying your real-time messages traverse the public internet to a routable hostname. That hostname is enumerable, which widens the attack surface for credential-stuffing and denial-of-service attempts against your messaging plane and leaves no way to constrain traffic to a known network. For a chat, notifications or live-collaboration backend this is the data plane itself: every message in flight depends on it, so an outage or interception here is felt by users immediately, not buried in a back-office batch job. A private link keeps that traffic on the Microsoft backbone, lets you later close the public endpoint entirely, and removes a route attackers can use to exfiltrate data out of the virtual network through the service. It also makes the messaging tier eligible for the same network controls and monitoring you already apply to the rest of the workload.
How do I fix "Azure SignalR Service should use private link"?
- Create a private endpoint that targets the SignalR resource with the 'signalr' group ID: az network private-endpoint create --resource-group <rg> --name <pe-name> --vnet-name <vnet> --subnet <subnet> --private-connection-resource-id <signalr-resource-id> --group-id signalr --connection-name <conn-name>.
- Link a Private DNS zone (privatelink.service.signalr.net) to the VNet and register the A record so clients resolve the SignalR hostname to the private endpoint rather than the public IP.
- Enforce the standard tenant-wide by assigning the built-in policy. Look the definition up by name rather than pasting a GUID: pid=$(az policy definition list --query "[?displayName=='Azure SignalR Service should use private link'].name" -o tsv); az policy assignment create --name signalr-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 SignalR Service should use private link" a false positive?
A SignalR instance running in the Serverless service mode for a pure client-to-client or function-triggered scenario may have no need for virtual-network ingress, yet the audit still flags it because no private endpoint exists. If that instance carries no sensitive payloads and is already locked down by its IP firewall rules, with public network access disabled, it is a deliberate and defensible exception. Record the rationale and apply a scoped policy exemption rather than re-architecting around connectivity the workload does not use.