Private endpoints: the basics
What does it mean for a PaaS service to still be on the public internet?
Almost every managed service in Azure ships with a public endpoint by default. Your storage account answers on a public DNS name, your SQL database listens on a public IP, and the same is true of an Azure Machine Learning workspace, an Event Grid topic or domain, a SignalR service and an App Configuration store. A service firewall can narrow who reaches that endpoint, but the endpoint itself is still resolvable and routable from the public internet. A private endpoint changes the shape of the problem: it projects the service into a subnet of your own virtual network as a private IP, so the service is reached over the Azure backbone rather than across the internet at all.
Microsoft Defender for Cloud turns the absence of a private endpoint into a recommendation per service, which is why one estate can fail several of these checks at once: 'Storage account should use a private link connection', 'Private endpoint connections on Azure SQL Database should be enabled', 'Azure Machine Learning workspaces should use private link', 'Azure Event Grid domains should use private link', 'Azure Event Grid topics should use private link', 'Azure SignalR Service should use private link' and 'App Configuration should use private link'. They read as seven separate findings, but they are one capability and one control: secure each cloud service with network controls so its data plane is reachable only privately. In the Microsoft Cloud Security Benchmark this is control NS-2, secure cloud services with network controls.
The work is not to bolt a private endpoint onto everything indiscriminately. It is to decide, service by service, which workloads should never be reachable from the internet, give each one a private endpoint into the right subnet, wire up the private DNS so the existing service name resolves to the private IP, and then close the public path so nothing quietly keeps using it. Done well, the application code does not change at all: it still uses the same hostname, but that hostname now resolves to an address that only your network can reach.
In this lesson you will learn what a private endpoint actually is, how it differs from a service firewall, how private DNS makes the change invisible to your application, and how to roll private endpoints out across Storage, SQL Database, Machine Learning, Event Grid, SignalR and App Configuration without breaking a live workload. The Controls this lesson covers section lists every Defender for Cloud recommendation in this capability, each linking to a deep page with the exact check and a copy-and-paste fix.
The firewall that was never the front door
A service firewall and a private endpoint are often confused, but they solve different problems. A firewall on a public endpoint still leaves the service resolvable and routable from the internet: it just refuses connections from addresses you have not allowed, which means a misconfigured rule, a forgotten 'allow all Azure services' toggle or a leaked allowed IP can re-open the door. A private endpoint does not narrow the public front door, it removes it: the service is projected as a private IP inside your network and, once public network access is disabled, there is no internet-facing endpoint left to misconfigure. That is why the Microsoft Cloud Security Benchmark treats private link as the stronger control and Defender for Cloud raises a recommendation for each service still relying only on its public endpoint.
Finding which services are still public across a subscription
Priya is the platform engineer at a fintech preparing for a SOC 2 audit. Defender for Cloud shows a spread of private-link findings: a storage account, a SQL database, an App Configuration store and a SignalR service, all still reachable on their public endpoints.
Rather than work the findings one at a time, Priya starts by listing which of these services already have a private endpoint and which do not, so the genuinely sensitive workloads can be prioritised before anything is rewired.
List every private endpoint in the subscription and the service each one connects to, so you can see which services are already private and which still rely on a public endpoint.
The services missing from this list are the ones still on a public endpoint. Cross-reference against the Defender findings and prioritise whatever holds or moves sensitive data.
How a private endpoint and private DNS actually workdeep dive
A private endpoint is a network interface that Azure places into a subnet of your virtual network and binds to a specific service through a private link service connection. The connection names a group ID, the sub-resource you are exposing, for example 'blob' or 'file' on a storage account, 'sqlServer' on a SQL logical server, 'configurationStores' on App Configuration, or 'signalr' on a SignalR service. Once created, the service has a private IP on your network, and the original public endpoint can be switched off. Some connections are auto-approved if you own both ends; cross-tenant or restricted ones land in a pending state and have to be approved with 'az network private-endpoint-connection approve'.
The piece that makes this invisible to your application is DNS. The service keeps its normal hostname, but Microsoft adds a 'privatelink' CNAME layer, so 'mystore.blob.core.windows.net' resolves through 'mystore.privatelink.blob.core.windows.net'. You link a private DNS zone, such as 'privatelink.blob.core.windows.net' for blob or 'privatelink.database.windows.net' for SQL, to your virtual network, and the private endpoint registers an A record in it pointing at the private IP. Inside the network the existing hostname now resolves to the private address; outside it, with public access disabled, it resolves to nothing usable. The application connection string never changes.
Defender for Cloud evaluates these recommendations against the Microsoft Cloud Security Benchmark on a periodic cycle, so adding an endpoint does not flip the finding to Healthy instantly: the control-plane change is immediate but the posture catches up on the next assessment. One important accuracy note: the older 'deny' style policy for some services, including the original Azure Machine Learning private-link policy, was deprecated, because a private endpoint is created after the resource exists, so a deny at creation time could never succeed. The current guidance audits for the private endpoint and pairs it with a 'configure ... with private endpoints' deployIfNotExists policy and a public-network-access restriction, rather than denying creation outright.
What is the impact of leaving PaaS services on public endpoints?
The direct impact is reachability. A storage account, SQL database, Event Grid topic, SignalR service or App Configuration store on a public endpoint is resolvable and routable from the internet, so the only thing standing between it and an attacker is the service firewall and its authentication. Firewalls are configuration, and configuration drifts: an over-broad rule, a leaked allowed IP, or the 'allow trusted Azure services' toggle left on can quietly widen access. A private endpoint removes the public path entirely, so there is no internet-facing endpoint left for a misconfiguration to expose.
The second-order impact is blast radius and lateral movement. App Configuration stores frequently hold connection strings and secrets references, SignalR relays live application traffic, and a SQL database or storage account is the data itself. A public endpoint on any of these is a foothold that, once reached, can be pivoted from. Taking the data and control planes onto the private backbone shrinks the reachable surface to your own network, which is the surface you can segment, monitor and defend.
On the compliance side, NS-2 in the Microsoft Cloud Security Benchmark, and the equivalent network-isolation expectations in ISO 27001, SOC 2, PCI DSS and the UK and EU data-protection regimes, all expect evidence that data services are not exposed to the public internet where private connectivity is available. A passing set of private-link recommendations across the estate is a concrete, defensible artefact: it demonstrates network isolation rather than merely asserting that a firewall was configured correctly.
How do you move services to private endpoints safely?
Work the capability as one loop rather than chasing individual findings. The order matters: stand up the private endpoint and its DNS, confirm the application still resolves and connects, and only then disable public access, so you never cut a live workload off from its own data.
1. Inventory which services are still public
List the services flagged by Defender alongside which ones already have a private endpoint. Treat the sensitivity of the data behind each service as the priority signal, not the finding count, because a customer database and a non-production store both surface as a single recommendation.
2. Create the endpoint with the right group ID and DNS
For each service, create a private endpoint into the correct subnet with the right sub-resource group ID (for example 'blob' or 'file' on storage, 'sqlServer' on SQL, 'configurationStores' on App Configuration, 'signalr' on SignalR). Link the matching private DNS zone to the virtual network and register the A record, so the existing hostname resolves to the private IP. Approve any pending connection if it was not auto-approved.
3. Verify, then disable public network access
From inside the network, confirm the service hostname now resolves to the private IP and the application connects. Only then set public network access to Disabled on the service, so there is no public endpoint left. Doing this in the reverse order would break the live workload before the private path is proven.
4. Ratchet it shut with Azure Policy
Assign the 'audit' private-link policies so any new service without a private endpoint is flagged, and pair them with the 'configure ... with private endpoints' deployIfNotExists policies and a public-network-access restriction, so the safe configuration is enforced rather than merely encouraged. Avoid the deprecated deny-at-creation policies, which cannot succeed because the endpoint is added after the resource exists.
# 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" Quick quiz
Question 1 of 5Defender for Cloud shows private-link findings on a storage account, a SQL database, an App Configuration store and a SignalR service. What is the most efficient way to think about them?
You scored
0 / 5
Keep learning
Go deeper on how private endpoints, private link and private DNS combine to take Azure PaaS services off the public internet.
- What is a private endpoint? How a private endpoint projects a service into your network as a private IP, and what a private link service connection is.
- Azure private endpoint DNS integration How the privatelink DNS zones make the existing service hostname resolve to the private IP, with the zone name for each service.
- Microsoft cloud security benchmark: Network Security (NS-2) The control this capability satisfies: secure cloud services with network controls by establishing a private access point.
You can now treat private endpoints as one capability rather than a scatter of findings: inventory which managed services are still on public endpoints, prioritise by the sensitivity of the data behind each, stand up a private endpoint and its DNS, verify the application connects privately, disable public network access, and ratchet the estate shut with audit and deployIfNotExists policies rather than the deprecated deny-at-creation ones. The Controls this lesson covers section below links every recommendation in this group to its deep page and fix.
Back to the library