The public door you never asked for
An Azure storage account has a public DNS name. By default, the Blob name can resolve to a public IP. That tells you where a connection would go, but not whether it can reach data: public network access, resource firewall rules, identity and keys are separate controls. If only private clients should reach storage, give them a private path, make their DNS point to it, then disable public network access after testing.
- Public endpoint
- A service-facing public IP reached through a name such as campuxstore.blob.core.windows.net. A successful DNS lookup does not prove the resource will accept a connection or grant data access.
Keep three jobs apart. Network: a private endpoint puts a private IP for a supported resource in your VNet. Name: the client must resolve the service name to that IP, often through a linked Private DNS zone. Access: disable unwanted public network access and keep identity or key checks. A service endpoint is different: the service name continues to resolve to a public IP and traffic uses Azure's backbone, so it does not need private-endpoint DNS.
Service endpoints: the half-measure, fairly
A virtual network service endpoint identifies traffic from selected subnets to a supported Azure service over Azure's backbone. You still configure the resource firewall to allow those subnets and reject others. DNS continues to return the service's public IP.
- Service endpoint
- A subnet setting used with a supported service's firewall rule. The service keeps its public IP; access is restricted only when its firewall is configured correctly.
A service endpoint does not give storage a private IP. With the resource firewall restricted to your subnet, unauthorized public requests should be rejected, even though the account's DNS still resolves to a public IP. The network path stays on Azure's backbone. The name, path and access decision are different facts.
Service endpoints can fit a policy that permits the service's public IP but limits access to selected Azure subnets. They do not provide a private IP for a client in a peered VNet or on premises. If policy requires public network access disabled and a private path for those clients, use a supported private endpoint and plan DNS and routing for each client network. Check service support and cost before choosing.
Private endpoints: a NIC of your own
A private endpoint, built on Azure Private Link, adds a network interface with a private IP in your VNet. It connects to a specific supported service or sub-resource. Adding it does not by itself change the resource's public network-access setting.
- Private endpoint
- A network interface with a private IP in your subnet that connects to a supported resource over Private Link. The public path is controlled separately.
For example, a Blob private endpoint might receive 10.20.2.10. A client must be able to route to that IP and resolve the Blob name to it. Test an authorized Blob request from the real client. Then set the storage account's public-network-access to Disabled and retest from inside and outside. Peered networks and on-premises clients need their own route and DNS design; the endpoint's existence alone does not configure those networks.
Private endpoints have hourly and data-processing charges; service endpoints have no separate endpoint charge. Check current regional pricing and expected traffic before choosing. A private endpoint can be healthy while the application still uses another route; the next section shows how to detect that.
| Question | Service endpoint | Private endpoint |
|---|---|---|
| Does the resource get a private IP? | No — keeps its public IP | Yes — a NIC in your subnet |
| Can public network access be disabled while retaining this path? | No — service endpoints use the public service address | Yes, where supported; disable it separately after testing |
| Reachable from on-prem / peered VNets? | No — bound to the subnet | Yes — it is just a private IP to route to |
| Needs the client name to resolve to a private IP? | No — DNS stays public | Yes — usually a linked Private DNS zone or a correct custom DNS path |
| Cost | Free | Per hour + data |
For "allow these Azure subnets, keep the public service address," a service endpoint plus resource firewall rules may fit. For "disable public network access and keep these clients connected," use a private endpoint for a supported service and verify the clients' route, DNS answer and credentials before cutover. A public DNS record can still exist after public access is disabled; the access setting decides whether public requests are accepted.
A. A service endpoint does not supply a private IP or change public DNS. It identifies subnet traffic over Azure's backbone; a separate resource firewall rule restricts who may use the public service address. There is no separate service-endpoint charge. A private endpoint adds a private IP, and public network access can then be disabled separately.
B. A private endpoint gives the app a private IP to use after public network access is disabled separately. The database's public DNS name may still exist, but public requests are rejected. A service endpoint and public-IP firewall allow lists do not provide the same private path. Verify the app's DNS, route and credentials.
The DNS trap that catches everyone
You create the private endpoint and see its private IP. But the application looks up the storage name through a resolver that still returns a public IP. If public network access is disabled, the application may fail to connect; if it remains enabled, the application may use the public service address. The endpoint itself can be healthy in both cases. Test DNS from the application network.
A private endpoint is useful only when the caller reaches it.
For Azure Blob Storage, the public DNS chain can include a privatelink.blob.core.windows.net CNAME after a private endpoint is created. A client using public DNS can still get a public IP. A client using a correctly linked Private DNS zone should get the endpoint's private IP. Custom DNS can work too when it forwards or answers the name correctly. The only reliable check is a lookup from the actual client network, followed by a real application request.1
- Private DNS zone
- An Azure DNS zone, such as privatelink.blob.core.windows.net for Blob Storage, that holds a record for the endpoint's private IP. A linked VNet can use it when its DNS configuration follows the Azure resolution path.
The rule is narrower and more useful: the caller's service name must resolve to the endpoint's private IP if that caller is meant to use the private path. A linked Private DNS zone is the common Azure solution, not the only one. A private-IP answer still does not prove the app can connect or has permission. Verify DNS, routing and a real request from the workload, then verify that public network access is disabled if your policy requires it.2
The pattern interviews ask for: web app to private storage
Assemble the pieces and you get the single most-asked private-networking design: a web application that reaches its storage account entirely privately. Four parts, and every one is load-bearing.
- VNet integration
- The web app is joined to the VNet for outbound traffic, so its requests originate inside the network — a PaaS app is not in a subnet by default, and without this its traffic never reaches a private IP.
- Private endpoint
- A NIC with a private IP for the storage account, placed in the data subnet — the private door into the resource.
- Private DNS zone
- privatelink.blob.core.windows.net, linked to the VNet, mapping the storage name to that private IP so the app resolves it correctly.
- Public access disabled
- On the storage account, so the only way in is the private endpoint — the step that turns "also reachable privately" into "reachable only privately."
Every part is boring alone and load-bearing together. The two labs at the end of this class build exactly this pattern — once with the CLI, once by hand in the portal — so you meet each piece twice, from both directions.
Campux takes storage private — and trips on DNS first
Campux creates a Blob private endpoint and disables public network access before testing the storefront. Images fail because the storefront still resolves campuxstore.blob.core.windows.net to a public IP. The endpoint shows Approved, but that status says nothing about the storefront's DNS or route.
Campux adds the privatelink.blob.core.windows.net zone, links it to the storefront VNet and connects it to the endpoint's DNS zone group.3 It checks the answer from the storefront network, tests an authorized Blob request, and repeats after public access is disabled. A custom DNS design could supply the same private-IP answer, but it would need the same tests.
Play it through
Add a private endpoint, make the storefront resolve its name to the private IP, test an authorized request, then disable public network access separately. The simulation stops when it needs your hands.
Create it the CLI way
Build a storage private endpoint and its DNS zone in a temporary resource group, then prove the private path from a small VM inside the network before you close the public door. Azure Cloud Shell can create the resources, but an ordinary Cloud Shell session is not a client inside this new VNet. Steps 6 to 8 create that client and test through it. Do not disable public access until it resolves the private IP and reads a file through it.
Open Cloud Shell — the >_ icon in the top bar of the portal — and choose Bash. (Locally instead: install the Azure CLI and run az login.)On screen: Cloud Shell already knows who you are, so there is no login step and the az command is ready. It is the fastest way to run these without installing anything.
Set names once, so the rest reads cleanly:
export MSYS_NO_PATHCONV=1 # Git Bash on Windows only; harmless elsewhere RG="campux-pe-$RANDOM"; LOC=eastus; SA="cxpe$(date +%s)$RANDOM" az group create -n "$RG" -l "$LOC" az storage account create -n "$SA" -g "$RG" -l "$LOC" --sku Standard_LRS az network vnet create -g "$RG" -n vnet-lab \ --address-prefixes 10.30.0.0/16 --subnet-name data --subnet-prefixes 10.30.2.0/24Create the private endpoint for the account's blob service, in the data subnet:
SA_ID=$(az storage account show -n "$SA" -g "$RG" --query id -o tsv) az network private-endpoint create -g "$RG" --name "pe-$SA-blob" \ --vnet-name vnet-lab --subnet data \ --private-connection-resource-id "$SA_ID" \ --group-id blob --connection-name "$SA-blob"
What just happened: a read-only NIC now sits in the data subnet with a private IP bound to that storage account. The corridor exists — but nothing resolves to it yet.Create the Private DNS zone, link it to the VNet, and wire it to the endpoint — the step that avoids the §4 trap:
az network private-dns zone create -g "$RG" -n privatelink.blob.core.windows.net az network private-dns link vnet create -g "$RG" \ --zone-name privatelink.blob.core.windows.net \ --name link-lab --virtual-network vnet-lab --registration-enabled false az network private-endpoint dns-zone-group create -g "$RG" \ --endpoint-name "pe-$SA-blob" --name default \ --zone-name blob --private-dns-zone privatelink.blob.core.windows.net
Put one file in the account while it is still public, so there is something to read later. This gives your own sign-in a data role on this one account (it needs Owner or User Access Administrator, which a personal subscription has). Role grants can take a few minutes to apply; if the upload says you lack permission, wait two minutes and run the last line again.
ME=$(az ad signed-in-user show --query id -o tsv) az role assignment create --assignee-object-id "$ME" --assignee-principal-type User \ --role "Storage Blob Data Contributor" --scope "$SA_ID" -o none az storage container create --account-name "$SA" -n lab --auth-mode login -o none echo "hello from the private path" > hello.txt az storage blob upload --account-name "$SA" -c lab -n hello.txt -f hello.txt --auth-mode login -o none
Create a client inside the network. Cloud Shell runs outside vnet-lab, so it cannot test the private path. A small VM with no public IP can, and you drive it with run-command, which needs no SSH. Give the VM its own identity and let that identity read blobs on this account only:
az network vnet subnet create -g "$RG" --vnet-name vnet-lab -n client \ --address-prefixes 10.30.1.0/24 -o none az vm create -g "$RG" -n vm-client --image Ubuntu2204 --size Standard_B1s \ --vnet-name vnet-lab --subnet client --public-ip-address "" --nsg "" \ --assign-identity --admin-username azureuser --generate-ssh-keys -o none VM_ID=$(az vm show -g "$RG" -n vm-client --query identity.principalId -o tsv) az role assignment create --assignee-object-id "$VM_ID" --assignee-principal-type ServicePrincipal \ --role "Storage Blob Data Reader" --scope "$SA_ID" -o none
If the VM is refused: an error with SkuNotAvailable means that size has no capacity for your subscription in this region right now (in our October 2026 run, both B1s and B2s were refused in eastus). List the sizes you can use, pick a small one, and rerun the az vm create line with it: az vm list-skus -l "$LOC" --resource-type virtualMachines --query "[?restrictions[0]==null && starts_with(name, 'Standard_D2a')].name" -o tsv. Standard_D2as_v4 worked in our run. Cost: B1s is about a cent an hour, D2as_v4 about ten cents; the teardown below removes it either way.Predict, then check DNS from inside. What address should the account's name resolve to from the VM: a public one, or one in 10.30.2.0/24?
az vm run-command invoke -g "$RG" -n vm-client --command-id RunShellScript \ --scripts "getent hosts $SA.blob.core.windows.net" --query "value[0].message" -o tsv
Checkpoint: the output shows a 10.30.2.x address beside the account's privatelink name. A public address here means the DNS zone link or zone group is missing; go back to the previous step. Compare with the record the zone holds: az network private-dns record-set a list -g "$RG" -z privatelink.blob.core.windows.net --query "[].{name:name, ip:aRecords[0].ipv4Address}" -o table. The two addresses should match.Make an authorised read from inside. This block writes a two-line script and sends it to the VM. There are no quotes around this EOF, so $SA is filled in now, while the backslashed \$T waits until the VM runs it. The script asks the VM's identity for a token, then reads the file with it:
cat > read.sh <<EOF T=\$(curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://storage.azure.com/" | python3 -c 'import sys,json;print(json.load(sys.stdin)["access_token"])') curl -s -H "Authorization: Bearer \$T" -H "x-ms-version: 2021-08-06" "https://$SA.blob.core.windows.net/lab/hello.txt" EOF az vm run-command invoke -g "$RG" -n vm-client --command-id RunShellScript \ --scripts @read.sh --query "value[0].message" -o tsv
Gate: the output includes hello from the private path. That is a private DNS answer, a private route and an authorised request, all from one real client. If you see an authorisation error instead, the VM's role grant has not applied yet; wait two minutes and run the last command again.Only after that gate passes, disable public network access. Then repeat the read from the VM, and try the same read from Cloud Shell, which is outside:
az storage account update -n "$SA" -g "$RG" \ --public-network-access Disabled --default-action Deny -o none az vm run-command invoke -g "$RG" -n vm-client --command-id RunShellScript \ --scripts @read.sh --query "value[0].message" -o tsv # still: hello from the private path az storage blob download --account-name "$SA" -c lab -n hello.txt -f out.txt \ --auth-mode login # refused from outside
Checkpoint: the VM still reads the file; the read from Cloud Shell fails with "The request may be blocked by network rules of storage account", even though your sign-in holds a data role. The refusal comes from the network rule, not from your permissions. This command does not remove the public DNS record; the name still resolves publicly, and the door behind it is closed.Tear it all down in one line, so nothing bills overnight:
az group delete -n "$RG" --yes --no-wait
Delete only the temporary group you created. If the Cloud Shell session ended, look up the exact group name before deleting.
Create it the portal way
Now build the identical thing by clicking, because the Create wizard's own tabs name the pieces better than any diagram. Do this with a throwaway storage account you can delete afterwards.
Go to portal.azure.com and sign in.On screen: the home page with a search bar running across the very top. Almost everything in Azure starts from that bar.
In the search bar at the top, type Private endpoints and select it from the results, then click + Create.On screen: the "Private endpoints" service page lists any that exist and offers Create. This launches the "Create a private endpoint" wizard, a row of tabs you move through left to right.
Basics tab: choose your subscription and a resource group (make a throwaway one), name the endpoint pe-campuxlab-blob, and set the region.On screen: the region field matters — it must match the virtual network you attach two tabs along, or that network will not appear in the list. The wizard is quietly enforcing that a private endpoint and its VNet share a region.
Resource tab: pick "Connect to an Azure resource in my directory", choose your storage account, and set Target sub-resource to blob.On screen: that sub-resource dropdown is the same "group id" you passed on the CLI. One storage account exposes blob, file, queue, table — each needs its own endpoint. You are making the blob service private here.
Virtual Network tab: select your VNet and the data subnet.On screen: this is where the endpoint's network interface is placed and given a private IP from the subnet — the abstract "private endpoint" becoming a concrete address like 10.30.2.4 on your own network.
DNS tab: if Azure offers Integrate with private DNS zone, choose Yes for this Azure-managed DNS lab.On screen: inspect the zone and VNet link created. Choosing No is possible when you will provide a correct custom DNS path; without either DNS setup, this VNet's client may still resolve the public IP.
Review + create, then Create. Wait for deployment, then open the endpoint and confirm it shows Approved.
Verify from a VM or VNet-integrated app that can reach the endpoint: check the storage name resolves to the private IP and make an authorized Blob request. Only after that succeeds, disable Public network access on storage. Repeat the request and test that an external client is rejected. If you have no such client, inspect the deployment but do not claim a successful cutover.On screen: an Approved endpoint, private DNS answer, successful private request and Disabled public network access are four separate checks.
Delete the resource group when finished so the endpoint, storage account and DNS zone all disappear together.
Portal wording and tab order drift over time; if a label here does not match your screen, the concept is the anchor — resource, sub-resource, subnet, and the DNS-integration toggle.
Read what the blades are telling you
Creating a thing and reading a thing are different skills, and interviews and incidents both test the second. Open what you built and let each blade explain a concept you just met.
Open the private endpoint resource and read its overview: the private IP, the VNet and subnet it lives in, and the resource it fronts.On screen: the "Network interface" link proves the point of the whole class — a private endpoint is a NIC. Follow it and you see an ordinary network card with a private IP, no different from a VM's.
Open the Private DNS zone privatelink.blob.core.windows.net and look at its records and its "Virtual network links".On screen: an A record maps your account name to the private IP, and the VNet link shows which networks resolve it. This is, concretely, the thing whose absence causes the §4 trap — you are looking at the fix.
Open the storage account → Networking blade and read the top of the page.On screen: "Public network access: Disabled" and a "Private endpoint connections" list with yours "Approved". Three settings tell the entire security story of this account at a glance.
From inside the VNet, run nslookup on the storage name once more and read the answer as a sentence: name → privatelink CNAME → private IP.On screen: the resolution chain from §4, printed in your own terminal. If you can narrate those three hops out loud, you understand private connectivity better than most people who have configured it.
Zoom out: "private" is a property of the whole path, not one setting
You can build a private endpoint. Now reason about what "going private" does to the whole system, because the claim is only as true as its most public leftover and its DNS.
An Approved endpoint does not prove the app uses it. Check the DNS answer and a real authorized request from the app, then test public rejection after cutover.
Private access depends on the endpoint IP, a route to it, correct client DNS and credentials. A broken shared DNS rule can affect many apps at once.
The security of the private path is bounded by its most public leftover — one forgotten public endpoint is the door, no matter how careful the rest.
Disabling public access before verifying private resolution is a self-inflicted outage; the order of operations is itself the risk.
Per-resource private endpoints and DNS zones done by hand become an unauditable web; policy has to enforce "private by default" before the estate outgrows the eye that would catch a gap.
To prove the design, show a private-IP DNS answer and a successful authorized request from the app, then show a public request being rejected.
Closing the finding with a refused connection
When asked whether the database still accepts public traffic, check its network setting and test it. Add a private endpoint and working DNS for the app, verify a real query, then disable public network access and retest both paths. The app may keep its connection string, but it can still fail if DNS, routing or credentials are wrong.
This is the whiteboard round of an interview. Rehearse it where it is cheap.
The endpoint can be Approved while the app uses another address. Draw the linked zone in this Azure DNS example and show the lookup from the storefront returning the endpoint IP. Add a real authorized Blob request and a public-access check; a DNS line alone does not prove data access or isolation.
Examination
The Whiteboard above, four drills here, then two situations. The situations have no marking scheme — write your answer before you reveal the reasoning, or the exercise is worthless. Nothing is stored; this is between you and the page.
B. A private endpoint is a NIC with a private IP in your subnet, connected to the one resource over Private Link — a genuine private presence inside your network. A describes a service endpoint, which only restricts access to the still-public address; that is the distinction the whole class turns on. C and D are inventions — no new public IP, no tunnel. If you can say "a NIC with a private IP," you can tell a private endpoint from everything that merely guards the public one.
B. A private endpoint gives those clients a private IP to reach after public network access is disabled. They still need routing, a private DNS answer and permission to use storage. A service endpoint uses the public service IP and cannot serve this requirement once that path is disabled. The public DNS name may remain resolvable even though public requests are rejected.
Private NIC, correct client DNS, and different charging models. Service endpoints do not give the PaaS resource a private IP. Private-endpoint DNS is not automatic for every caller: a linked Private DNS zone is common, but correctly configured custom DNS can also work. Check current prices for your region and traffic.
PLAN — take campuxstore private
1. Create a private endpoint for the blob service in the data subnet.
2. Leave public network access enabled during the migration.
3. App keeps using campuxstore.blob.core.windows.net, unchanged.
4. (No Private DNS zone is created or linked to the VNet.)
Line four. The app needs its usual storage name to resolve to the private endpoint's IP from its own network. A linked Private DNS zone is one way; a correct custom DNS setup is another. Without either, the app may continue using the public IP while public access remains enabled.
Next, test an authorized Blob request over the private path. Only then disable public network access and retest. If you disabled it before fixing DNS, the app could fail instead of falling back.
Inventory the callers. For each account, identify Blob, File, Queue or Table use, the app networks and any batch jobs. Create the required private endpoints and DNS records; configure the caller's route to the private IP.
Test from the actual workloads. Confirm each name resolves privately and make authorized service requests from every important caller. An Approved endpoint or an nslookup from Cloud Shell is not proof for the storefront.
Cut over gradually. Disable public network access one account at a time, repeat app tests, watch failures and keep a rollback plan. Test that an external client is rejected. Then use policy and monitoring to prevent new public exposure. No sequence can guarantee zero disruption without testing the actual clients.
Use the requirement to decide. A service endpoint has no separate endpoint charge and uses Azure's backbone. With resource firewall rules, it can allow selected Azure subnets. It does not supply a private IP or work as a private path after public network access is disabled.
Match the mandate. Here public network access must be disabled while the app keeps using storage. A private endpoint supplies the private route; DNS must point the app to it. Disable public access separately after a successful request.
Price the real design. Check current endpoint and data charges for the region, plus DNS and operations. Automate the repeated configuration and test it. Neither a low price nor a private-IP diagram proves the application works.
What Part I leaves you holding
- A public DNS answer does not prove public data access. Check network rules and credentials separately.
- A service endpoint restricts a still-public resource to your subnets — free, backbone-routed, and never able to remove the public address.
- A private endpoint adds a private IP; public network access must be disabled separately when required.
- The caller must resolve the service name to that private IP. A linked Private DNS zone is common; custom DNS can also work.
- Verify route, DNS, an authorized request and public-access settings from the actual clients before and after cutover.
- Microsoft's private-endpoint DNS guidance explains the public CNAME chain and private-zone configuration. A public DNS answer is separate from whether the service accepts a request. ↩
- For hybrid callers, follow Microsoft's DNS integration guidance to forward the relevant names to a resolver that can answer for the Azure private zone. Class Fifteen covers the hybrid design; this lab assumes a VNet-connected test client. ↩
- Group IDs and DNS zones vary by service and sub-resource. Consult Microsoft's current zone table before building more than Blob Storage. ↩
Build a private endpoint, verify client DNS and an authorised request, then disable public access and retest.
Play the lesson above to stamp this class.