Microsoft Defender for Cloud · Azure Machine Learning
Azure Machine Learning workspaces should use private link
Written and reviewed by Emnode · Last reviewed
What does the recommendation "Azure Machine Learning workspaces should use private link" check?
Audits each Azure Machine Learning workspace for at least one approved Private Link private endpoint connection, so the workspace's data plane (the studio, the v2 API and the SDK) is reachable over private IP addresses inside a virtual network rather than over a public endpoint. It inspects the workspace's privateEndpointConnections, not whether public access is also still switched on, so a workspace can satisfy this control yet remain publicly reachable. It does not check the dependency services a workspace relies on, so the linked Storage account, Key Vault and Container Registry can still be wide open even when the workspace itself passes.
Why does "Azure Machine Learning workspaces should use private link" matter?
A Machine Learning workspace is a data exfiltration target: it holds datastores, registered datasets, model artefacts and training outputs that frequently contain customer or proprietary data. With a public endpoint, anyone who obtains a token or a misconfigured role can reach that data plane from the open internet, and the workspace becomes a path to pull data straight out of your tenant. A private endpoint confines workspace traffic to your virtual network and the Azure backbone, so a leaked credential is far less useful without network access. The business consequence of getting this wrong is a reportable breach of training data plus the regulated personal data that often sits inside it.
How do I fix "Azure Machine Learning workspaces should use private link"?
- On a new workspace, set 'public_network_access: Disabled' in the workspace YAML and create it with 'az ml workspace create -g <resource-group-name> --file privatelink.yml' so it starts life private.
- Add the private endpoint with 'az network private-endpoint create --name <pe-name> --vnet-name <vnet> --subnet <subnet> --group-id amlworkspace --connection-name workspace --private-connection-resource-id <workspace-resource-id>', then create the privatelink.api.azureml.ms and privatelink.notebooks.azure.net private DNS zones and link them to the virtual network so names resolve to the private IPs.
- Lock down an existing public workspace with 'az ml workspace update --set public_network_access=Disabled -n <workspace-name> -g <resource-group-name>', and assign the built-in policy to keep it that way by looking the definition up by name rather than hardcoding a GUID: pid=$(az policy definition list --query "[?displayName=='Azure Machine Learning workspaces should use private link'].name" -o tsv); az policy assignment create --name aml-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 "Azure Machine Learning workspaces should use private link" a false positive?
A throwaway sandbox workspace that holds only synthetic sample data and is deleted at the end of each lab can reasonably stay public, because the cost of standing up a virtual network, private endpoint and DNS zones outweighs the risk to data that is fabricated and disposable. Exempt that one workspace explicitly and keep private link mandatory everywhere a workspace can touch real data.