Skip to content

Instantly share code, notes, and snippets.

@maskati
Last active October 21, 2025 14:28
Show Gist options
  • Select an option

  • Save maskati/e56d1bf6b4cc0e14f77d409e7f158e45 to your computer and use it in GitHub Desktop.

Select an option

Save maskati/e56d1bf6b4cc0e14f77d409e7f158e45 to your computer and use it in GitHub Desktop.

Revisions

  1. maskati revised this gist Oct 21, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion #pls-directconnect.md
    Original file line number Diff line number Diff line change
    @@ -7,7 +7,7 @@ Microsoft recently released in public preview Private Link Service [Direct Conne
    > [!IMPORTANT]
    > To use direct connect you need to enable the feature flag `Microsoft.Network/AllowPrivateLinkserviceUDR` on your subscription e.g. using `az feature register --namespace Microsoft.Network --name AllowPrivateLinkserviceUDR`. Review the [direct connect prerequisites](https://learn.microsoft.com/en-us/azure/private-link/configure-private-link-service-direct-connect#prerequisites) for more details.
    [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/)
    [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fgistcdn1.captainz.cc%2Fmaskati%2Fe56d1bf6b4cc0e14f77d409e7f158e45%2Fraw%2Fd1a9c37821fda09b6cd83aedf82fba728cecb970%2Farm.json)

    The example deploys the following:

  2. maskati created this gist Oct 21, 2025.
    54 changes: 54 additions & 0 deletions #pls-directconnect.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,54 @@
    Until recently Azure Private Link has been restricted to [specific Microsoft enabled resource types](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview#private-link-resource) or your own [Azure VM hosted resources](https://learn.microsoft.com/en-us/azure/private-link/private-link-service-overview).

    A notable limitation has been the requirement for the destination to be a [Standard Load Balancer with a backend pool configured by NIC](https://learn.microsoft.com/en-us/azure/private-link/private-link-service-overview#limitations). This excluded services such as [VNet integrated Azure Container Instances](https://learn.microsoft.com/en-us/azure/container-instances/container-instances-virtual-network-concepts), which do not support private endpoints and also do not provision a NIC in the VNet and therefore could only be routed by IP address.

    Microsoft recently released in public preview Private Link Service [Direct Connect](https://learn.microsoft.com/en-us/azure/private-link/configure-private-link-service-direct-connect). Direct Connect allows connectivity to any privately routable destination IP address. To demonstrate this new feature we can publish a private Azure Container Instance over Azure Private Link Service direct connect.

    > [!IMPORTANT]
    > To use direct connect you need to enable the feature flag `Microsoft.Network/AllowPrivateLinkserviceUDR` on your subscription e.g. using `az feature register --namespace Microsoft.Network --name AllowPrivateLinkserviceUDR`. Review the [direct connect prerequisites](https://learn.microsoft.com/en-us/azure/private-link/configure-private-link-service-direct-connect#prerequisites) for more details.
    [![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/)

    The example deploys the following:

    ```mermaid
    flowchart LR
    subgraph VNET1["vnet-pls: 10.0.0.0/16"]
    subgraph SN1["subnet-aci: 10.0.1.0/24"]
    ACI1["Container Instance / nginx 10.0.1.4"]
    end
    subgraph SN2["subnet-pls: 10.0.2.0/24"]
    subgraph PLS["Private Link Service"]
    NAT1["NAT IP 10.0.2.4"]
    NAT2["NAT IP 10.0.2.5"]
    end
    end
    NAT1 -- "Direct connect" --> ACI1
    NAT2 -- "Direct connect" --> ACI1
    end
    subgraph VNET2["vnet-pe: 10.0.0.0/16"]
    subgraph SN3["subnet-aci: 10.0.1.0/24"]
    ACI2["Container Instance / client 10.0.1.4"]
    end
    subgraph SN4["subnet-pe: 10.0.2.0/24"]
    PE["Private Endpoint 10.0.2.4"]
    end
    end
    subgraph DNS["DNS: directconnect.test"]
    A["A ping -> 10.0.2.4"]
    end
    ACI2 -- "curl http://ping.directconnect.test" --> PE
    PE -- "Private Endpoint Connection" --> PLS
    VNET2 --- DNS
    DNS -.- PE
    ```

    We can verify connectivity by running a curl command using [az container exec](https://learn.microsoft.com/en-us/cli/azure/container?view=azure-cli-latest#az-container-exec) (see the `curlCommand` deployment output parameter), noting that connections are distributed across private link service NAT addresses:

    ```sh
    ~ % az container exec --ids '/subscriptions/.../resourceGroups/.../providers/Microsoft.ContainerInstance/containerGroups/aci-pe' --exec-command 'curl http://ping.directconnect.test.'
    2025-10-21T10:32:14+00:00 remote_addr=10.0.2.4

    ~ % az container exec --ids '/subscriptions/.../resourceGroups/.../providers/Microsoft.ContainerInstance/containerGroups/aci-pe' --exec-command 'curl http://ping.directconnect.test.'
    2025-10-21T10:32:20+00:00 remote_addr=10.0.2.5
    ```
    441 changes: 441 additions & 0 deletions arm.json
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,441 @@
    {
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "languageVersion": "2.0",
    "contentVersion": "1.0.0.0",
    "metadata": {
    "_generator": {
    "name": "bicep",
    "version": "0.38.33.27573",
    "templateHash": "18102201612663974453"
    }
    },
    "parameters": {
    "location": {
    "type": "string",
    "allowedValues": [
    "northcentralus",
    "eastus",
    "centralus",
    "southcentralus",
    "westus",
    "westus2",
    "westus3",
    "asiasoutheast",
    "australiaeast",
    "spaincentral"
    ]
    }
    },
    "variables": {
    "$fxv#0": "d29ya2VyX3Byb2Nlc3NlcyAxOwoKZXZlbnRzIHsKICAgIHdvcmtlcl9jb25uZWN0aW9ucyAxMDI0Owp9CgpodHRwIHsKICAgIGRlZmF1bHRfdHlwZSB0ZXh0L3BsYWluOwogICAgc2VydmVyIHsKICAgICAgICBsaXN0ZW4gODA7CiAgICAgICAgbG9jYXRpb24gLyB7CiAgICAgICAgICAgICMgcmVtb3RlX2FkZHIgaXMgdGhlIHByaXZhdGUgbGluayBzZXJ2aWNlIG5hdGVkIGlwCiAgICAgICAgICAgIHJldHVybiAyMDAgIiR0aW1lX2lzbzg2MDEgcmVtb3RlX2FkZHI9JHJlbW90ZV9hZGRyXG4iOwogICAgICAgIH0KICAgIH0KfQo=",
    "vnetPlsAddressPrefix": "10.0.0.0/16",
    "vnetPeAddressPrefix": "10.0.0.0/16"
    },
    "resources": {
    "vnetPls::subnetAci": {
    "existing": true,
    "type": "Microsoft.Network/virtualNetworks/subnets",
    "apiVersion": "2024-10-01",
    "name": "[format('{0}/{1}', 'vnet-pls', 'subnet-aci')]",
    "dependsOn": [
    "vnetPls"
    ]
    },
    "vnetPls::subnetPls": {
    "existing": true,
    "type": "Microsoft.Network/virtualNetworks/subnets",
    "apiVersion": "2024-10-01",
    "name": "[format('{0}/{1}', 'vnet-pls', 'subnet-pls')]",
    "dependsOn": [
    "vnetPls"
    ]
    },
    "vnetPe::subnetAci": {
    "existing": true,
    "type": "Microsoft.Network/virtualNetworks/subnets",
    "apiVersion": "2024-10-01",
    "name": "[format('{0}/{1}', 'vnet-pe', 'subnet-aci')]",
    "dependsOn": [
    "vnetPe"
    ]
    },
    "vnetPe::subnetPe": {
    "existing": true,
    "type": "Microsoft.Network/virtualNetworks/subnets",
    "apiVersion": "2024-10-01",
    "name": "[format('{0}/{1}', 'vnet-pe', 'subnet-pe')]",
    "dependsOn": [
    "vnetPe"
    ]
    },
    "dnsDirectConnectTest::linkVnetPe": {
    "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks",
    "apiVersion": "2024-06-01",
    "name": "[format('{0}/{1}', 'directconnect.test', 'link-vnet-pe')]",
    "location": "global",
    "properties": {
    "registrationEnabled": false,
    "virtualNetwork": {
    "id": "[resourceId('Microsoft.Network/virtualNetworks', 'vnet-pe')]"
    }
    },
    "dependsOn": [
    "dnsDirectConnectTest",
    "vnetPe"
    ]
    },
    "vnetPls": {
    "type": "Microsoft.Network/virtualNetworks",
    "apiVersion": "2024-10-01",
    "name": "vnet-pls",
    "location": "[parameters('location')]",
    "properties": {
    "addressSpace": {
    "addressPrefixes": [
    "[variables('vnetPlsAddressPrefix')]"
    ]
    },
    "subnets": [
    {
    "name": "subnet-aci",
    "properties": {
    "addressPrefix": "[cidrSubnet(variables('vnetPlsAddressPrefix'), 24, 1)]",
    "delegations": [
    {
    "name": "dMicrosoft.ContainerInstance/containerGroups",
    "properties": {
    "serviceName": "Microsoft.ContainerInstance/containerGroups"
    }
    }
    ]
    }
    },
    {
    "name": "subnet-pls",
    "properties": {
    "addressPrefix": "[cidrSubnet(variables('vnetPlsAddressPrefix'), 24, 2)]",
    "privateLinkServiceNetworkPolicies": "Disabled"
    }
    }
    ]
    }
    },
    "aciPls": {
    "type": "Microsoft.ContainerInstance/containerGroups",
    "apiVersion": "2025-09-01",
    "name": "aci-pls",
    "location": "[parameters('location')]",
    "properties": {
    "sku": "Standard",
    "osType": "Linux",
    "restartPolicy": "OnFailure",
    "ipAddress": {
    "type": "Private",
    "ports": [
    {
    "port": 80
    }
    ]
    },
    "subnetIds": [
    {
    "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet-pls', 'subnet-aci')]"
    }
    ],
    "volumes": [
    {
    "name": "nginxconfvolume",
    "secret": {
    "nginx.conf": "[variables('$fxv#0')]"
    }
    }
    ],
    "containers": [
    {
    "name": "nginx",
    "properties": {
    "image": "mcr.microsoft.com/azurelinux/base/nginx:1.25",
    "command": [
    "nginx",
    "-g",
    "daemon off;",
    "-c",
    "/mnt/nginx.conf"
    ],
    "resources": {
    "requests": {
    "cpu": "[json('0.1')]",
    "memoryInGB": "[json('0.1')]"
    }
    },
    "ports": [
    {
    "port": 80
    }
    ],
    "volumeMounts": [
    {
    "name": "nginxconfvolume",
    "mountPath": "/mnt"
    }
    ]
    }
    }
    ]
    },
    "dependsOn": [
    "vnetPls"
    ]
    },
    "plsDirectConnect": {
    "type": "Microsoft.Network/privateLinkServices",
    "apiVersion": "2024-10-01",
    "name": "pls-directconnect",
    "location": "[parameters('location')]",
    "properties": {
    "destinationIPAddress": "[reference('aciPls').ipAddress.ip]",
    "enableProxyProtocol": false,
    "ipConfigurations": [
    {
    "name": "ipconfig1",
    "properties": {
    "primary": true,
    "privateIPAddressVersion": "IPv4",
    "privateIPAllocationMethod": "Static",
    "privateIPAddress": "[cidrHost(reference('vnetPls::subnetPls').addressPrefix, 3)]",
    "subnet": {
    "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet-pls', 'subnet-pls')]"
    }
    }
    },
    {
    "name": "ipconfig2",
    "properties": {
    "primary": false,
    "privateIPAddressVersion": "IPv4",
    "privateIPAllocationMethod": "Static",
    "privateIPAddress": "[cidrHost(reference('vnetPls::subnetPls').addressPrefix, 4)]",
    "subnet": {
    "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet-pls', 'subnet-pls')]"
    }
    }
    }
    ]
    },
    "dependsOn": [
    "aciPls",
    "vnetPls",
    "vnetPls::subnetPls"
    ]
    },
    "vnetPe": {
    "type": "Microsoft.Network/virtualNetworks",
    "apiVersion": "2024-10-01",
    "name": "vnet-pe",
    "location": "[parameters('location')]",
    "properties": {
    "addressSpace": {
    "addressPrefixes": [
    "[variables('vnetPeAddressPrefix')]"
    ]
    },
    "subnets": [
    {
    "name": "subnet-aci",
    "properties": {
    "addressPrefix": "[cidrSubnet(variables('vnetPeAddressPrefix'), 24, 1)]",
    "delegations": [
    {
    "name": "Microsoft.ContainerInstance/containerGroups",
    "properties": {
    "serviceName": "Microsoft.ContainerInstance/containerGroups"
    }
    }
    ]
    }
    },
    {
    "name": "subnet-pe",
    "properties": {
    "addressPrefix": "[cidrSubnet(variables('vnetPeAddressPrefix'), 24, 2)]"
    }
    }
    ]
    }
    },
    "aciPe": {
    "type": "Microsoft.ContainerInstance/containerGroups",
    "apiVersion": "2025-09-01",
    "name": "aci-pe",
    "location": "[parameters('location')]",
    "properties": {
    "sku": "Standard",
    "osType": "Linux",
    "restartPolicy": "Never",
    "ipAddress": {
    "type": "Private",
    "ports": [
    {
    "port": 65534,
    "protocol": "UDP"
    }
    ]
    },
    "subnetIds": [
    {
    "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet-pe', 'subnet-aci')]"
    }
    ],
    "containers": [
    {
    "name": "azurelinux",
    "properties": {
    "image": "mcr.microsoft.com/azurelinux/base/core:3.0",
    "command": [
    "sleep",
    "infinity"
    ],
    "resources": {
    "requests": {
    "cpu": "[json('0.1')]",
    "memoryInGB": "[json('0.1')]"
    }
    },
    "ports": [
    {
    "port": 65534,
    "protocol": "UDP"
    }
    ]
    }
    }
    ]
    },
    "dependsOn": [
    "vnetPe"
    ]
    },
    "peDirectConnect": {
    "type": "Microsoft.Network/privateEndpoints",
    "apiVersion": "2024-10-01",
    "name": "pe-directconnect",
    "location": "[parameters('location')]",
    "properties": {
    "subnet": {
    "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', 'vnet-pe', 'subnet-pe')]"
    },
    "privateLinkServiceConnections": [
    {
    "name": "pls-connection",
    "properties": {
    "privateLinkServiceId": "[resourceId('Microsoft.Network/privateLinkServices', 'pls-directconnect')]",
    "requestMessage": "Approve connection for direct connect"
    }
    }
    ]
    },
    "dependsOn": [
    "plsDirectConnect",
    "vnetPe"
    ]
    },
    "dnsDirectConnectTest": {
    "type": "Microsoft.Network/privateDnsZones",
    "apiVersion": "2024-06-01",
    "name": "directconnect.test",
    "location": "global"
    },
    "dns": {
    "type": "Microsoft.Resources/deployments",
    "apiVersion": "2025-04-01",
    "name": "[format('dns-{0}', uniqueString('dns', deployment().name))]",
    "properties": {
    "expressionEvaluationOptions": {
    "scope": "inner"
    },
    "mode": "Incremental",
    "parameters": {
    "dnsAName": {
    "value": "ping"
    },
    "dnsRid": {
    "value": "[resourceId('Microsoft.Network/privateDnsZones', 'directconnect.test')]"
    },
    "nicRid": {
    "value": "[reference('peDirectConnect').networkInterfaces[0].id]"
    }
    },
    "template": {
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "metadata": {
    "_generator": {
    "name": "bicep",
    "version": "0.38.33.27573",
    "templateHash": "12135959616729773951"
    }
    },
    "parameters": {
    "nicRid": {
    "type": "string",
    "minLength": 116,
    "metadata": {
    "description": "The resource ID of the private endpoint network interface"
    }
    },
    "dnsRid": {
    "type": "string",
    "minLength": 116,
    "metadata": {
    "description": "The resource ID of the private DNS zone"
    }
    },
    "dnsAName": {
    "type": "string",
    "minLength": 1,
    "maxLength": 253,
    "metadata": {
    "description": "The A record name to create in the private DNS zone"
    }
    }
    },
    "resources": [
    {
    "type": "Microsoft.Network/privateDnsZones/A",
    "apiVersion": "2024-06-01",
    "name": "[format('{0}/{1}', split(parameters('dnsRid'), '/')[8], parameters('dnsAName'))]",
    "properties": {
    "aRecords": [
    {
    "ipv4Address": "[reference(resourceId('Microsoft.Network/networkInterfaces', split(parameters('nicRid'), '/')[8]), '2024-10-01').ipConfigurations[0].properties.privateIPAddress]"
    }
    ],
    "ttl": 300
    }
    }
    ],
    "outputs": {
    "ipAddress": {
    "type": "string",
    "value": "[reference(resourceId('Microsoft.Network/networkInterfaces', split(parameters('nicRid'), '/')[8]), '2024-10-01').ipConfigurations[0].properties.privateIPAddress]"
    },
    "fqdn": {
    "type": "string",
    "value": "[reference(resourceId('Microsoft.Network/privateDnsZones/A', split(parameters('dnsRid'), '/')[8], parameters('dnsAName')), '2024-06-01').fqdn]"
    }
    }
    }
    },
    "dependsOn": [
    "dnsDirectConnectTest",
    "peDirectConnect"
    ]
    }
    },
    "outputs": {
    "curlCommand": {
    "type": "string",
    "value": "[format('az container exec --ids ''{0}'' --exec-command ''curl http://{1}''', resourceId('Microsoft.ContainerInstance/containerGroups', 'aci-pe'), reference('dns').outputs.fqdn.value)]"
    }
    }
    }