Skip to main content

Theoretical Foundation: Configure Private Endpoints for Azure PaaS


1. Initial Intuition​

In the previous module about Service Endpoints, we saw that they create an optimized path from the VNet to the PaaS service, but the service maintains its public IP address. It's like having an exclusive entrance to a bank, but the bank still has an address on the main street that anyone can visit.

Private Endpoints go much further: they bring the PaaS service inside your VNet. It's as if the bank opened an exclusive branch inside your condominium, with an internal address that only residents know and only they can visit. The original bank continues to exist on the main street, but your private branch is only accessible from inside.

In practice: a Private Endpoint creates a network interface (NIC) with a private IP from your VNet that represents a specific PaaS service (like a Storage Account or a SQL Database). When your VMs connect to the service, DNS resolves the service name to this private IP, and traffic never leaves your private network.


2. Context​

2.1 The evolution of private access to PaaS​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

2.2 Why Private Endpoints are superior to Service Endpoints​

Private Endpoints solve limitations that Service Endpoints cannot address:

  • On-premises access: VMs or servers connected via VPN or ExpressRoute can access PaaS services as if they were internal resources
  • Private DNS: The name myaccount.blob.core.windows.net resolves to a private IP in the VNet, not to a public IP
  • Instance-level isolation: Each Private Endpoint points to a specific service instance, not to the entire Azure Storage service
  • Elimination of public endpoint: It's possible to completely disable public access to the PaaS service

2.3 Main use cases​

  • Banking and healthcare applications with regulated data that can never travel over public networks
  • On-premises environments connected via ExpressRoute that need to access Azure SQL or Azure Storage
  • Architectures where the PaaS service should not be visible outside the VNet
  • Multi-tenant environments where different clients have completely isolated PaaS services

3. Concept Construction​

3.1 The three components of a Private Endpoint​

1. Private Endpoint (the resource) An Azure object that represents the private connection to a service. Contains: the NIC with private IP, the reference to the target service, and the target sub-resource.

2. Network Interface (NIC) Created automatically along with the Private Endpoint. Receives a private IP from the subnet where it's deployed. It's through this NIC that traffic actually flows.

3. Private Link Connection The logical connection between the Private Endpoint and the PaaS service. May require approval by the service owner, depending on configuration.


3.2 Sub-resources: connecting to the right resource within the service​

Many PaaS services have multiple sub-resources. When you create a Private Endpoint, you need to specify which sub-resource you want to expose. Examples:

ServiceAvailable sub-resources
Azure Storageblob, file, queue, table, dfs, web
Azure SQL DatabasesqlServer
Azure Key Vaultvault
Azure Cosmos DBSql, MongoDB, Cassandra, Gremlin, Table
Azure App Servicesites
Azure Container Registryregistry
Azure Event Hubs Namespacenamespace

Important point: If you need a Storage Account to be accessed via both Blob and File through private networks, you need two Private Endpoints, one for the blob sub-resource and another for file.


There's a distinction that often causes confusion:

Private Endpoint: The consumer side. You create it in your VNet to access a service from another party (Azure PaaS or partner service).

Private Link Service: The provider side. An organization creates it to expose their own services (running behind a Standard Load Balancer) to other VNets or tenants via Private Endpoints.

For the AZ-104 objective, the focus is on Private Endpoints connecting to Microsoft-managed PaaS services.


3.4 The connection approval process​

When a Private Endpoint is created pointing to a PaaS service, the connection can have different states:

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

Auto-approval: When the Private Endpoint and target service are in the same Azure subscription or the creator has Microsoft.Network/privateEndpoints/privateLinkServiceConnections/write permission on the service, approval is automatic.

Manual approval: When the service belongs to another tenant or another subscription without the above permissions, the service owner must manually approve in the Private endpoint connections panel of the resource.


3.5 Private DNS Zone: the most critical piece of the puzzle​

This is the most important and least intuitive component of Private Endpoints.

The problem: The name myaccount.blob.core.windows.net publicly resolves to a public IP. If your VMs use this name and it resolves to the public IP, traffic will go over the internet even if you have a Private Endpoint.

The solution: A Private DNS Zone that overrides DNS resolution for this name within your VNet, making it resolve to the Private Endpoint's private IP.

Each PaaS service has a specific private DNS zone:

ServiceSub-resourcePrivate DNS zone
Azure Storage (Blob)blobprivatelink.blob.core.windows.net
Azure Storage (File)fileprivatelink.file.core.windows.net
Azure Storage (Queue)queueprivatelink.queue.core.windows.net
Azure Storage (Table)tableprivatelink.table.core.windows.net
Azure Storage (ADLS Gen2)dfsprivatelink.dfs.core.windows.net
Azure SQL DatabasesqlServerprivatelink.database.windows.net
Azure Key Vaultvaultprivatelink.vaultcore.azure.net
Azure Cosmos DB (SQL)Sqlprivatelink.documents.azure.com
Azure Container Registryregistryprivatelink.azurecr.io
Azure App Servicesitesprivatelink.azurewebsites.net

The mechanism works like this: when you resolve myaccount.blob.core.windows.net from within the VNet:

  1. The DNS query goes to the VNet's DNS resolver (168.63.129.16)
  2. The resolver checks if there's a Private DNS Zone linked to the VNet for privatelink.blob.core.windows.net
  3. If yes, and if there's an A record for myaccount.privatelink.blob.core.windows.net β†’ returns the Private Endpoint's private IP
  4. Traffic goes to the private IP (the Private Endpoint's NIC), never leaving the VNet

4. Structural View​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

5. Practical Operation​

5.1 Complete configuration flow​

100%
Scroll para zoom Β· Arraste para mover Β· πŸ“± Pinch para zoom no celular

Most forgotten step: Step 4, linking the Private DNS Zone to the VNet. The zone can exist but if it's not linked to the VNet, the VNet's DNS resolver doesn't use it and the name continues resolving to the public IP.


5.2 The Azure portal creates the DNS Zone automatically​

When you create a Private Endpoint through the portal and select "Integrate with private DNS zone", the portal:

  1. Creates the Private DNS Zone with the correct name automatically
  2. Creates the VNet link automatically
  3. Creates the A record pointing to the private IP automatically

This greatly simplifies the process. Via CLI and IaC, each step is separate and manual.


5.3 Verifying DNS resolution​

After configuring, verify that DNS is correct from within a VM in the VNet:

# On Linux (inside the VM)
nslookup myaccount.blob.core.windows.net

# Expected result:
# myaccount.blob.core.windows.net β†’ myaccount.privatelink.blob.core.windows.net β†’ 10.0.2.5
# (The intermediate CNAME with "privatelink" is normal and expected)

The CNAME to privatelink.blob.core.windows.net is created automatically by Microsoft in public DNS. The Private DNS Zone intercepts the resolution of this CNAME and returns the private IP.


5.4 On-premises access via ExpressRoute/VPN​

For on-premises servers to access the PaaS service via Private Endpoint:

  1. The Private Endpoint must exist and be approved
  2. The Private DNS Zone must be linked to the VNet
  3. On-premises DNS needs to be configured to forward queries for privatelink.blob.core.windows.net to the VNet's DNS resolver (168.63.129.16)

This DNS forwarding configuration is the most complex point in hybrid environments. Azure Private DNS Resolver simplifies this by creating inbound and outbound endpoints for DNS resolution.


6. Implementation Methods​

6.1 Azure Portal​

When to use: Initial creation, simple environments, when you want automatic DNS Zone creation.

Creation: Private Link Center > Private endpoints > + Create

OR from the storage Account: Storage Account > Networking > Private endpoint connections > + Private endpoint

The form goes through tabs:

  • Basics: Name, region, resource group
  • Resource: Resource type, target resource, sub-resource
  • Virtual Network: VNet, subnet, IP configuration
  • DNS: Integration with Private DNS Zone (recommended to check "Yes")
  • Tags and Review

6.2 Azure CLI​

Creating the Private Endpoint:

# Get Storage Account Resource ID
STORAGE_ID=$(az storage account show \
--name myaccount \
--resource-group myRG \
--query id --output tsv)

# Create the Private Endpoint
az network private-endpoint create \
--name pe-myaccount-blob \
--resource-group myRG \
--vnet-name myVNet \
--subnet PrivateEndpointSubnet \
--private-connection-resource-id $STORAGE_ID \
--group-id blob \
--connection-name conn-myaccount-blob \
--location eastus

Creating the Private DNS Zone:

az network private-dns zone create \
--resource-group myRG \
--name "privatelink.blob.core.windows.net"

Linking the DNS Zone to VNet:

az network private-dns link vnet create \
--resource-group myRG \
--zone-name "privatelink.blob.core.windows.net" \
--name link-to-myVNet \
--virtual-network myVNet \
--registration-enabled false

Creating the DNS record automatically using DNS Zone Group:

# Get the Private Endpoint Resource ID
PE_ID=$(az network private-endpoint show \
--name pe-myaccount-blob \
--resource-group myRG \
--query id --output tsv)

# Create DNS Zone Group (creates A record automatically)
az network private-endpoint dns-zone-group create \
--resource-group myRG \
--endpoint-name pe-myaccount-blob \
--name dns-zone-group \
--private-dns-zone "privatelink.blob.core.windows.net" \
--zone-name zone1

DNS Zone Group is the mechanism that automatically creates the A record in the Private DNS Zone and keeps it synchronized if the Private Endpoint's IP changes.

Disabling public access to Storage Account:

az storage account update \
--name myaccount \
--resource-group myRG \
--public-network-access Disabled

Checking connection status:

az network private-endpoint show \
--name pe-myaccount-blob \
--resource-group myRG \
--query "privateLinkServiceConnections[].privateLinkServiceConnectionState"

6.3 Azure PowerShell​

# Complete configuration via PowerShell

# 1. Create Private Endpoint
$storage = Get-AzStorageAccount -ResourceGroupName "myRG" -Name "myaccount"

$privateEndpointConnection = New-AzPrivateLinkServiceConnection `
-Name "conn-myaccount-blob" `
-PrivateLinkServiceId $storage.Id `
-GroupId "blob"

$subnet = Get-AzVirtualNetworkSubnetConfig `
-VirtualNetwork (Get-AzVirtualNetwork -Name "myVNet" -ResourceGroupName "myRG") `
-Name "PrivateEndpointSubnet"

$privateEndpoint = New-AzPrivateEndpoint `
-ResourceGroupName "myRG" `
-Name "pe-myaccount-blob" `
-Location "eastus" `
-Subnet $subnet `
-PrivateLinkServiceConnection $privateEndpointConnection

# 2. Create and configure Private DNS Zone
$zone = New-AzPrivateDnsZone `
-ResourceGroupName "myRG" `
-Name "privatelink.blob.core.windows.net"

New-AzPrivateDnsVirtualNetworkLink `
-ResourceGroupName "myRG" `
-ZoneName "privatelink.blob.core.windows.net" `
-Name "link-to-myVNet" `
-VirtualNetworkId (Get-AzVirtualNetwork -Name "myVNet" -ResourceGroupName "myRG").Id `
-EnableRegistration $false

# 3. Create DNS Zone Group
New-AzPrivateEndpointIpConfiguration `
-Name "dns-zone-group" `
-PrivateDnsZoneId $zone.ResourceId `
-RecordType A

6.4 Bicep​

// Private Endpoint for Storage Account (Blob)
resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-05-01' = {
name: 'pe-myaccount-blob'
location: location
properties: {
subnet: {
id: privateEndpointSubnet.id
}
privateLinkServiceConnections: [
{
name: 'conn-myaccount-blob'
properties: {
privateLinkServiceId: storageAccount.id
groupIds: ['blob']
}
}
]
}
}

// Private DNS Zone
resource privateDnsZone 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.blob.core.windows.net'
location: 'global'
}

// VNet Link
resource vnetLink 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
parent: privateDnsZone
name: 'link-to-vnet'
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: vnet.id
}
}
}

// DNS Zone Group (creates A record automatically)
resource dnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01' = {
parent: privateEndpoint
name: 'dns-zone-group'
properties: {
privateDnsZoneConfigs: [
{
name: 'config-blob'
properties: {
privateDnsZoneId: privateDnsZone.id
}
}
]
}
}

6.5 Approving pending connection (cross-tenant)​

When the Private Endpoint points to a service in another tenant or subscription without adequate permissions:

# On the provider side (service owner), list pending connections
az storage account show \
--name myaccount \
--resource-group myRG \
--query privateEndpointConnections

# Approve the connection
az network private-endpoint-connection approve \
--resource-group myRG \
--resource-name myaccount \
--type Microsoft.Storage/storageAccounts \
--name <connection-name>

7. Control and Security​

7.1 Required permissions​

OperationMinimum permission
Create Private EndpointNetwork Contributor on VNet + Reader on PaaS service
Approve Private Endpoint connectionContributor or Owner on PaaS service
Create Private DNS ZonePrivate DNS Zone Contributor
Link DNS Zone to VNetNetwork Contributor on VNet
Disable public access to PaaSContributor on PaaS resource

7.2 NSG and Private Endpoints​

By default, NSGs do not apply to Private Endpoints. Traffic to the Private Endpoint's private IP passes through the subnet NSG but the rules are not evaluated.

To enable NSG on Private Endpoints (newer functionality):

az network vnet subnet update \
--vnet-name myVNet \
--name PrivateEndpointSubnet \
--resource-group myRG \
--private-endpoint-network-policies Enabled

After enabling, NSGs and UDRs applied to the subnet affect traffic to Private Endpoints, allowing additional microsegmentation.


7.3 Disabling public access to the PaaS service​

This is the final step for maximum security. After creating the Private Endpoint, the PaaS service still accepts public connections by default. To disable:

Storage Account:

az storage account update \
--name myaccount \
--resource-group myRG \
--public-network-access Disabled

Azure SQL Database:

az sql server update \
--name mySqlServer \
--resource-group myRG \
--enable-public-network false

Key Vault:

az keyvault update \
--name myKeyVault \
--resource-group myRG \
--public-network-access Disabled

Warning: Before disabling public access, ensure that all applications and administrative tools accessing the service are already using the Private Endpoint. Premature disabling causes interruptions.


8. Decision Making​

8.1 Private Endpoint vs Service Endpoint: definitive decision​

CriteriaService EndpointPrivate Endpoint
Service IPPublic (immutable)Private (in VNet)
On-premises accessNoYes
CostFreeHourly cost + data
DNS configurationNot requiredMandatory
Disable public endpointOptional, but PaaS still accessibleCan disable completely
Isolation per instanceNo (per service)Yes (per specific resource)
ComplexityLowMedium/High
Transitivity via VNet peeringNoYes (traffic via route)
Regulatory support (HIPAA, PCI-DSS)PartialComplete

SituationApproachJustification
ExpressRoute access to StoragePrivate Endpoint (mandatory)Service Endpoint is not transitive via ExpressRoute
Azure VM accesses SQL Database, no on-premisesService Endpoint or Private EndpointBoth work; PE if compliance requirement
PCI-DSS or HIPAA compliancePrivate Endpoint (mandatory)Data cannot traverse public networks at all
Multi-tenant environment with complete isolationPrivate Endpoint (mandatory)Service Endpoint doesn't isolate per instance
Minimum cost in dev/testService EndpointPE has additional cost
Private DNS resolution requiredPrivate Endpoint (mandatory)Service Endpoint doesn't change DNS

8.3 Dedicated subnet for Private Endpoints​

SituationUse dedicated subnet?Reason
Multiple Private EndpointsYes (recommended)Clearer organization and NSG management
Single Private EndpointOptionalCan coexist with other VMs if NSG is managed
Environment with NSG on PE enabledYesFacilitates creation of specific rules

9. Best Practices​

  • Always create the DNS Zone Group when creating the Private Endpoint via CLI/PowerShell/Bicep. Without it, the A record in the Private DNS Zone is not created automatically and DNS doesn't work.
  • Verify DNS resolution from within a VM in the VNet after configuration. An nslookup that returns a private IP is the fastest sanity check.
  • Disable public access to the PaaS service after validating that all applications work via Private Endpoint. Keep enabled during transition.
  • Use dedicated subnets for Private Endpoints in production, especially when there are many endpoints.
  • Centralize Private DNS Zones in the hub in hub-and-spoke architectures and link to spoke VNets. Don't create duplicate zones in each VNet.
  • Configure DNS forwarding for on-premises environments: local DNS servers should forward queries for privatelink.* to the Azure VNet DNS resolver.
  • Use Azure Private DNS Resolver in complex hybrid environments to centralize DNS resolution between on-premises and Azure.
  • Enable NSG on Private Endpoints in high-security environments to control which traffic can reach the endpoint.
  • Document which private IP was assigned to each Private Endpoint. IPs don't change after creation but it's important to record for troubleshooting.

10. Common Errors​

ErrorWhy it happensHow to avoid
DNS resolves public IP even with PE createdDNS Zone not linked to VNet or DNS Zone Group not createdCheck VNet link and A record in Private DNS Zone
Application can't connect even with correct DNSPublic access disabled before PE is approvedWait for approval and check connection state
Connection timeout from on-premisesOn-premises DNS doesn't forward privatelink.* queries to AzureConfigure DNS forwarder to the VNet
PE in "Pending" state indefinitelyCreator doesn't have auto-approval permission on the serviceManually approve in the PaaS service panel
Multiple DNS Zones created accidentallyEach deployment creates a new zone instead of reusingCheck if the zone already exists before creating
NSG blocking traffic to PEprivate-endpoint-network-policies enabled without adequate rulesCreate Allow rules in NSG for traffic to PE IP
A record not created in Private DNS ZoneDNS Zone Group was not createdCreate DNS Zone Group after creating Private Endpoint
Access from spoke VNet doesn't workDNS Zone not linked to spoke VNetAdd VNet link from zone to each VNet that needs to resolve

11. Operations and Maintenance​

11.1 Checking the state of all Private Endpoint connections​

# List all Private Endpoints and their states
az network private-endpoint list \
--resource-group myRG \
--query "[].{Name:name, Status:privateLinkServiceConnections[0].privateLinkServiceConnectionState.status, ProvisioningState:provisioningState}" \
--output table

11.2 Checking the created DNS record​

az network private-dns record-set a list \
--resource-group myRG \
--zone-name "privatelink.blob.core.windows.net" \
--output table

11.3 Monitoring Private Endpoints with Azure Monitor​

az monitor diagnostic-settings create \
--name "pe-diagnostics" \
--resource <private-endpoint-id> \
--metrics '[{"category": "AllMetrics", "enabled": true}]' \
--workspace <log-analytics-workspace-id>

Relevant metrics include bytes sent/received by the Private Endpoint.

11.4 Important limits​

ResourceLimit
Private Endpoints per subscription64,000
Private Endpoints per VNetNo documented limit
Private DNS Zones per subscription1,000
VNet links per Private DNS Zone1,000
A records per Private DNS Zone25,000
Cost~$7.30/month per endpoint + $0.01 per GB processed (approximate, varies by region)

12. Integration and Automation​

12.1 Azure Policy to require Private Endpoints​

# Policy: Storage Accounts must use Private Endpoints
az policy assignment create \
--name "storage-require-private-endpoint" \
--policy "6edd7eda-6dd8-40f7-810d-67160c639cd9" \
--scope "/subscriptions/<sub-id>"

Custom policy to deny creation of Storage Accounts with public access enabled:

{
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"field": "Microsoft.Storage/storageAccounts/publicNetworkAccess",
"notEquals": "Disabled"
}
]
},
"then": {
"effect": "deny"
}
}

12.2 Azure Private DNS Resolver for hybrid environments​

# Create DNS Resolver
az dns-resolver create \
--name myDnsResolver \
--resource-group myRG \
--location eastus \
--id-virtual-network <vnet-id>

# Create inbound endpoint (on-premises DNS points to this IP)
az dns-resolver inbound-endpoint create \
--dns-resolver-name myDnsResolver \
--resource-group myRG \
--name inbound-ep \
--ip-configurations "[{\"privateIpAllocationMethod\":\"Dynamic\",\"id\":\"<subnet-id>\"}]"

# Create outbound endpoint and forwarding ruleset
az dns-resolver outbound-endpoint create \
--dns-resolver-name myDnsResolver \
--resource-group myRG \
--name outbound-ep \
--id <subnet-id>

The Private DNS Resolver is Microsoft's recommended solution for hybrid DNS resolution, replacing the traditional "custom DNS server" with manual forwarders.


13. Final Summary​

Essential concepts:

  • A Private Endpoint creates a NIC with private IP in your VNet that represents a specific PaaS service. Traffic to the service never leaves the private network.
  • Configuration requires three components: the Private Endpoint, the Private DNS Zone (with VNet link), and the DNS Zone Group (which creates the A record automatically).
  • Each sub-resource of a PaaS service (blob, file, queue in Storage; sqlServer in SQL) requires a separate Private Endpoint.

The DNS chain is the central piece:

myaccount.blob.core.windows.net β†’ CNAME β†’ myaccount.privatelink.blob.core.windows.net β†’ A record in Private DNS Zone β†’ Private Endpoint private IP (e.g., 10.0.2.5)

Critical differences with Service Endpoints:

  • Service Endpoint: traffic uses MS backbone but PaaS has public IP. Doesn't work from on-premises.
  • Private Endpoint: PaaS receives private IP in VNet. Works from on-premises via VPN/ExpressRoute. Has additional cost. Requires DNS configuration.

What needs to be remembered:

  • Without the Private DNS Zone linked to the VNet, DNS resolves the public IP even with the Private Endpoint active.
  • The DNS Zone Group is the mechanism that creates and synchronizes the A record automatically.
  • The connection may need manual approval when the PaaS service is in another tenant or subscription without adequate permissions.
  • Disabling public access to the PaaS is the final step for complete security, but should be done after validating that everything works through the private endpoint.
  • For on-premises environments, DNS resolution requires configuring forwarders pointing to Azure DNS (168.63.129.16) or using Azure Private DNS Resolver.
  • By default, NSGs don't apply to Private Endpoints; it's necessary to enable private-endpoint-network-policies on the subnet to activate them.