Microsoft Defender for Cloud · Azure App Configuration
App Configuration should use private link
Written and reviewed by Emnode · Last reviewed
What does the recommendation "App Configuration should use private link" check?
Audits each Azure App Configuration store for at least one private endpoint connection whose connection state is 'Approved', so that clients can reach the store over a private IP from your virtual network instead of its public endpoint. This is an audit-only check (the policy effect is AuditIfNotExists): it only confirms an approved private endpoint exists. It does NOT verify that public network access has been turned off, so a store can have a private endpoint and still pass this control while 'publicNetworkAccess' remains Enabled and reachable from the internet. It also does not check firewall IP rules, the contents of stored keys, or whether the private DNS zone resolves correctly.
Why does "App Configuration should use private link" matter?
App Configuration stores hold connection strings, feature flags and environment settings that often act as the index to the rest of your estate. On the public endpoint, every store is reachable from the internet and protected only by access keys or Microsoft Entra tokens, so a leaked key or a token-theft attack is enough to read your whole configuration. A private endpoint pins access to your virtual network: traffic stays on the Azure backbone, the store stops appearing in internet-wide scans, and you can apply network security group rules to govern which subnets reach it. The business consequence of skipping it is that a single credential leak exposes the configuration for many downstream apps at once, turning a minor secret spill into a broad data-exposure incident that is hard to scope after the fact.
How do I fix "App Configuration should use private link"?
- Create a private endpoint for the store: 'az network private-endpoint create --resource-group <rg> --name <pe-name> --vnet-name <vnet> --subnet <subnet> --private-connection-resource-id <store-resource-id> --group-id configurationStores --connection-name <conn-name>', then link a privatelink.azconfig.io private DNS zone so clients resolve the private IP.
- Because the audit passes on the endpoint alone, also close the public door: 'az appconfig update --resource-group <rg> --name <store-name> --enable-public-network false' so requests are only accepted through the private endpoint.
- Enforce it going forward by assigning the built-in policy, looking the definition up by display name rather than pasting a GUID: pid=$(az policy definition list --query "[?displayName=='App Configuration should use private link'].name" -o tsv); az policy assignment create --name appconfig-private-link --policy "$pid" --scope <subscription-or-mg-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 "App Configuration should use private link" a false positive?
A store reached only through a private endpoint hosted in a peered hub virtual network can still flag if that endpoint's connection sits in a 'Pending' state rather than 'Approved', because the control counts only approved connections. Approve the pending connection on the store's Networking blade; the control then clears without any change to how the store is actually accessed. The same can happen during a manual private endpoint approval workflow across subscriptions, where the consumer creates the endpoint but the store owner has not yet approved it.