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

Microsoft Defender for Cloud · Azure Storage

Storage account should use a private link connection

Written and reviewed by Emnode · Last reviewed

What does the recommendation "Storage account should use a private link connection" check?

Flags storage accounts that have no approved private endpoint connection, meaning all access still routes over the account's public endpoint rather than a private link on the Microsoft backbone. The check looks for the existence of at least one accepted private endpoint, not for whether the public endpoint is firewalled. It is audit-only, so it does not create the endpoint for you, and it does not verify that you have a separate endpoint for every sub-resource you use (blob, file, queue, table, dfs or web each need their own).

Why does "Storage account should use a private link connection" matter?

Without a private endpoint, the storage account resolves to a public IP and is reachable from the internet by default, so security rests entirely on access keys, SAS tokens and any firewall rules you remember to set. A leaked key or an over-permissive network rule then exposes data directly to the public internet. A private link connection keeps traffic inside your virtual network and the Azure backbone, which lets you disable public access altogether and shrinks the attack surface to clients you actually peer with. For regulated workloads, the absence of a private path is also a common audit finding.

How do I fix "Storage account should use a private link connection"?

  1. Create a private endpoint for each sub-resource you use, naming the target group with --group-id (blob, file, queue, table, dfs or web): az network private-endpoint create --name pe-storage-blob --resource-group <rg> --vnet-name <vnet> --subnet <subnet> --private-connection-resource-id <storage-account-id> --group-id blob --connection-name storage-blob-conn.
  2. Wire up DNS by linking the matching privatelink.blob.core.windows.net private DNS zone to the VNet so clients resolve the account to its private IP, then lock the account down with az storage account update --name <account> --resource-group <rg> --public-network-access Disabled.
  3. Enforce the baseline across the estate by assigning the built-in audit policy. Look it up by display name rather than hardcoding the GUID: pid=$(az policy definition list --query "[?displayName=='Storage accounts should use private link'].name" -o tsv); az policy assignment create --name require-storage-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 "Storage account should use a private link connection" a false positive?

A storage account used only for static website hosting or public content distribution behind Azure Front Door has no need for a private endpoint, so it will flag even though that is the correct design. Exempt those accounts from the policy assignment rather than adding an endpoint you will never connect to.