Storage Must Stay Private? Create a Private Endpoint
An Azure Storage account has a public endpoint by default. Authentication still protects the data, but the endpoint remains reachable from the Internet. A workload that must use private connectivity needs a different boundary.
Create a private-only storage account and connect its Blob service to
snet-data through Azure Private Link:
WEB02 in snet-app
|
| storage-account.blob.core.windows.net resolves privately
v
Private Endpoint NIC in snet-data
|
`-- Azure Storage Blob service
Internet client ---> Public endpoint ---> Blocked
This trip builds on Private Service Needs a Private Name? Create a Private DNS Zone. Keep
vnet-cloudtrips-test-weu,snet-app,snet-data, and WEB02. The custominternal.cloudtrips.devzone is not used by Storage; Azure Storage requires its service-specific Private Link zone.
Understand the Three Controls
A complete private Storage path combines three independent controls:
| Control | Purpose |
|---|---|
| Private endpoint | Gives the Blob service a private IP in the VNet |
| Private DNS zone | Resolves the normal Blob hostname to that private IP inside the VNet |
| Disabled public network access | Rejects data-plane traffic through the public endpoint |
The private endpoint does not replace authentication. Clients still need an access key, SAS, or Microsoft Entra data-plane role to read and write data.
This trip creates an endpoint for the blob subresource only. File shares,
queues, tables, and Data Lake Storage dfs operations require their own
private endpoints and DNS zones.
Generate a Storage Account Name
Storage account names are globally unique. In Cloud Shell, select Bash and generate a stable name from the current subscription:
STORAGE_ACCOUNT="stctpe$(az account show --query id --output tsv | tr -d '-' | cut -c1-10)"
printf 'Storage account: %s\n' "$STORAGE_ACCOUNT"
Copy the displayed value. It contains only lowercase letters and numbers and fits the storage-account naming limit.
Create Private-Only Storage
In the Azure portal, search for Storage accounts, select + Create, and configure the Basics tab:
Subscription: CloudTrips TEST
Resource group: rg-cloudtrips-network-test-weu
Storage account name: the STORAGE_ACCOUNT value
Region: West Europe
Performance: Standard
Redundancy: Locally-redundant storage (LRS)
On Networking, set:
Public network access: Disable
Network routing: Microsoft network routing
Select Review + create > Create, then open the storage account. Disabling public access blocks the Storage data plane; it does not prevent the Azure control plane from managing the account.

The equivalent CLI command is:
az storage account create \
--resource-group rg-cloudtrips-network-test-weu \
--name "$STORAGE_ACCOUNT" \
--location westeurope \
--sku Standard_LRS \
--kind StorageV2 \
--https-only true \
--min-tls-version TLS1_2 \
--allow-blob-public-access false \
--public-network-access Disabled
Use either the portal or the CLI, not both.
Create the Blob Private Endpoint
In the storage account, open Security + networking > Networking > Private endpoint connections, then select + Private endpoint.
On Basics, configure:
Subscription: CloudTrips TEST
Resource group: rg-cloudtrips-network-test-weu
Name: pep-cloudtrips-storage-blob-test-weu
Network interface name: nic-pep-cloudtrips-storage-blob-test-weu
Region: West Europe
On Resource, keep Connect to an Azure resource in my directory and select:
Resource type: Microsoft.Storage/storageAccounts
Resource: the STORAGE_ACCOUNT value
Target sub-resource: blob
On Virtual Network, configure:
Virtual network: vnet-cloudtrips-test-weu
Subnet: snet-data
Private IP configuration: Dynamically allocate IP address
Application security group: None
On DNS, keep Integrate with private DNS zone set to Yes. Confirm:
Private DNS zone: privatelink.blob.core.windows.net
Resource group: rg-cloudtrips-network-test-weu
Select Review + create > Create. Because you own both resources, Azure normally approves the private endpoint connection automatically.

Verify the Endpoint and DNS Zone
Return to the storage account’s Private endpoint connections page. Confirm
that pep-cloudtrips-storage-blob-test-weu has status Approved.
Open Private DNS zones > privatelink.blob.core.windows.net. Confirm that:
- an A record named after the storage account points to a
10.20.2.xaddress - a virtual network link connects the zone to
vnet-cloudtrips-test-weu
The VM requests the normal <storage-account>.blob.core.windows.net hostname,
which DNS resolves to <storage-account>.privatelink.blob.core.windows.net inside the linked VNet. The A record in the private DNS zone then resolves that private name to the private endpoint’s 10.20.2.x IP address.
Azure manages the A record through the private endpoint’s DNS zone group. Do not replace it with a manually maintained record.

Resolve and Connect from the VNet
Start WEB02 if it is deallocated:
az vm start \
--resource-group rg-cloudtrips-network-test-weu \
--name vm-cloudtrips-web02-test-weu
Run DNS and HTTPS checks from that VM:
az vm run-command invoke \
--resource-group rg-cloudtrips-network-test-weu \
--name vm-cloudtrips-web02-test-weu \
--command-id RunShellScript \
--scripts "getent ahostsv4 ${STORAGE_ACCOUNT}.blob.core.windows.net | head -1; curl --silent --show-error --output /dev/null --write-out 'remote_ip=%{remote_ip} http_code=%{http_code}\n' https://${STORAGE_ACCOUNT}.blob.core.windows.net/"
Both results should show the same 10.20.2.x private endpoint address. An HTTP
400 or 403 response is acceptable here: it proves that HTTPS reached the
Blob service, while the unauthenticated request was not authorized.

Prove Public Access Is Blocked
Cloud Shell is outside vnet-cloudtrips-test-weu. Run:
curl --silent --show-error --include \
"https://${STORAGE_ACCOUNT}.blob.core.windows.net/?comp=list" \
| head -20
The request should be rejected with HTTP 403. Public DNS may still return a
public Azure address; private-only means that the public data endpoint refuses
the connection, not that its public hostname disappears.

Confirm the account setting and private endpoint connection explicitly:
az storage account show \
--resource-group rg-cloudtrips-network-test-weu \
--name "$STORAGE_ACCOUNT" \
--query publicNetworkAccess \
--output tsv
az network private-endpoint show \
--resource-group rg-cloudtrips-network-test-weu \
--name pep-cloudtrips-storage-blob-test-weu \
--query 'privateLinkServiceConnections[].{Name:name,Status:privateLinkServiceConnectionState.status}' \
--output table
Expect Disabled and a private endpoint connection with status Approved.

Keep or Remove the Private Endpoint Lab
Keep the resources if the next trip will use private Storage. Deallocate WEB02 when you no longer need it:
az vm deallocate \
--resource-group rg-cloudtrips-network-test-weu \
--name vm-cloudtrips-web02-test-weu
To remove only this lab, delete the private endpoint, storage account, and the
Storage-specific Private DNS zone. Keep the VNet, subnets, VMs, and
internal.cloudtrips.dev zone for other networking trips.