← Back to Tutorials
Advanced⏱️ 45 min

VNet Integration and Private Endpoints

VNet Integration and Private Endpoints

Why VNet Integration Matters

In regulated industries such as healthcare, finance, and government, data must remain within private networks and never traverse the public internet. VNet integration enables:

  • Compliance: Meet regulatory requirements (HIPAA, PCI-DSS, SOX) by keeping traffic on private networks
  • Security: Eliminate public endpoint exposure for backend services
  • Private backends: Connect to on-premises systems, databases, and services that are only accessible within a virtual network
  • Data exfiltration prevention: Ensure sensitive data cannot leave your controlled network boundary

Architecture Overview

┌─────────────────────────────────────────────────────────┐
│                    Azure Virtual Network                  │
│                      (10.0.0.0/16)                       │
│                                                          │
│  ┌──────────────────┐    ┌─────────────────────────┐    │
│  │  Integration      │    │  Private Endpoints       │    │
│  │  Subnet           │    │  Subnet                  │    │
│  │  (10.0.1.0/24)   │    │  (10.0.2.0/24)          │    │
│  │                   │    │                          │    │
│  │  ┌─────────────┐ │    │  ┌───────────────────┐  │    │
│  │  │ Logic App   │ │    │  │ Private Endpoint  │  │    │
│  │  │ Standard    │─┼────┼──│ (Service Bus)     │  │    │
│  │  │             │ │    │  └───────────────────┘  │    │
│  │  │             │─┼────┼──┌───────────────────┐  │    │
│  │  │             │ │    │  │ Private Endpoint  │  │    │
│  │  └─────────────┘ │    │  │ (Storage Account) │  │    │
│  │                   │    │  └───────────────────┘  │    │
│  └──────────────────┘    └─────────────────────────┘    │
│                                                          │
└─────────────────────────────────────────────────────────┘

All traffic between the Logic App and backend services stays within the VNet — no public internet exposure.

Prerequisites

  • Azure subscription with Contributor access
  • Azure CLI installed (v2.50+)
  • Logic App Standard (Workflow Service Plan or App Service Plan)
  • Existing Service Bus namespace and Storage Account (or create new ones)

Step 1: Create a VNet with Subnets

Create a virtual network with two subnets — one for Logic App integration and one for private endpoints.

# Set variables
RESOURCE_GROUP="rg-integration-hub"
LOCATION="eastus"
VNET_NAME="vnet-integration"

# Create the VNet
az network vnet create \
  --resource-group $RESOURCE_GROUP \
  --name $VNET_NAME \
  --location $LOCATION \
  --address-prefix 10.0.0.0/16

# Create subnet for Logic App VNet integration
az network vnet subnet create \
  --resource-group $RESOURCE_GROUP \
  --vnet-name $VNET_NAME \
  --name snet-logicapp-integration \
  --address-prefix 10.0.1.0/24 \
  --delegations Microsoft.Web/serverFarms

# Create subnet for private endpoints
az network vnet subnet create \
  --resource-group $RESOURCE_GROUP \
  --vnet-name $VNET_NAME \
  --name snet-private-endpoints \
  --address-prefix 10.0.2.0/24 \
  --disable-private-endpoint-network-policies true

Screenshot: VNet and subnets created in Azure Portal

Note: The integration subnet requires the Microsoft.Web/serverFarms delegation. The private endpoint subnet needs private endpoint network policies disabled.

Step 2: Enable VNet Integration on Logic App Standard

Connect your Logic App Standard to the integration subnet.

LOGIC_APP_NAME="logic-integration-hub"

# Enable VNet integration
az webapp vnet-integration add \
  --resource-group $RESOURCE_GROUP \
  --name $LOGIC_APP_NAME \
  --vnet $VNET_NAME \
  --subnet snet-logicapp-integration

Configure the Logic App to route all outbound traffic through the VNet:

# Route all traffic through VNet
az webapp config appsettings set \
  --resource-group $RESOURCE_GROUP \
  --name $LOGIC_APP_NAME \
  --settings WEBSITE_VNET_ROUTE_ALL=1

# Use private DNS for name resolution
az webapp config appsettings set \
  --resource-group $RESOURCE_GROUP \
  --name $LOGIC_APP_NAME \
  --settings WEBSITE_DNS_SERVER=168.63.129.16

Screenshot: VNet integration enabled on Logic App

Step 3: Create Private Endpoint for Service Bus

Create a private endpoint so the Logic App connects to Service Bus over the private network.

SERVICE_BUS_NAMESPACE="sb-integration-hub"

# Get the Service Bus resource ID
SB_RESOURCE_ID=$(az servicebus namespace show \
  --resource-group $RESOURCE_GROUP \
  --name $SERVICE_BUS_NAMESPACE \
  --query id --output tsv)

# Create private endpoint for Service Bus
az network private-endpoint create \
  --resource-group $RESOURCE_GROUP \
  --name pe-servicebus \
  --vnet-name $VNET_NAME \
  --subnet snet-private-endpoints \
  --private-connection-resource-id $SB_RESOURCE_ID \
  --group-id namespace \
  --connection-name pec-servicebus

Disable public network access on the Service Bus namespace:

az servicebus namespace update \
  --resource-group $RESOURCE_GROUP \
  --name $SERVICE_BUS_NAMESPACE \
  --set publicNetworkAccess="Disabled"

Screenshot: Private endpoint for Service Bus

Step 4: Create Private Endpoint for Storage Account

STORAGE_ACCOUNT="stintegrationhub"

# Get the Storage Account resource ID
STORAGE_RESOURCE_ID=$(az storage account show \
  --resource-group $RESOURCE_GROUP \
  --name $STORAGE_ACCOUNT \
  --query id --output tsv)

# Create private endpoint for blob storage
az network private-endpoint create \
  --resource-group $RESOURCE_GROUP \
  --name pe-storage-blob \
  --vnet-name $VNET_NAME \
  --subnet snet-private-endpoints \
  --private-connection-resource-id $STORAGE_RESOURCE_ID \
  --group-id blob \
  --connection-name pec-storage-blob

# Create private endpoint for table storage (used by Logic Apps runtime)
az network private-endpoint create \
  --resource-group $RESOURCE_GROUP \
  --name pe-storage-table \
  --vnet-name $VNET_NAME \
  --subnet snet-private-endpoints \
  --private-connection-resource-id $STORAGE_RESOURCE_ID \
  --group-id table \
  --connection-name pec-storage-table

# Create private endpoint for queue storage
az network private-endpoint create \
  --resource-group $RESOURCE_GROUP \
  --name pe-storage-queue \
  --vnet-name $VNET_NAME \
  --subnet snet-private-endpoints \
  --private-connection-resource-id $STORAGE_RESOURCE_ID \
  --group-id queue \
  --connection-name pec-storage-queue

# Create private endpoint for file storage
az network private-endpoint create \
  --resource-group $RESOURCE_GROUP \
  --name pe-storage-file \
  --vnet-name $VNET_NAME \
  --subnet snet-private-endpoints \
  --private-connection-resource-id $STORAGE_RESOURCE_ID \
  --group-id file \
  --connection-name pec-storage-file

Screenshot: Private endpoints for Storage Account

Important: Logic Apps Standard requires private endpoints for blob, table, queue, and file storage sub-resources to function correctly in a fully private configuration.

Step 5: DNS Configuration for Private Endpoints

Private endpoints require DNS configuration so that service FQDNs resolve to private IP addresses instead of public ones.

Create Private DNS Zones

# Create private DNS zone for Service Bus
az network private-dns zone create \
  --resource-group $RESOURCE_GROUP \
  --name privatelink.servicebus.windows.net

# Create private DNS zone for Storage (blob, table, queue, file)
az network private-dns zone create \
  --resource-group $RESOURCE_GROUP \
  --name privatelink.blob.core.windows.net

az network private-dns zone create \
  --resource-group $RESOURCE_GROUP \
  --name privatelink.table.core.windows.net

az network private-dns zone create \
  --resource-group $RESOURCE_GROUP \
  --name privatelink.queue.core.windows.net

az network private-dns zone create \
  --resource-group $RESOURCE_GROUP \
  --name privatelink.file.core.windows.net

Link DNS Zones to VNet

# Link Service Bus DNS zone
az network private-dns link vnet create \
  --resource-group $RESOURCE_GROUP \
  --zone-name privatelink.servicebus.windows.net \
  --name link-servicebus \
  --virtual-network $VNET_NAME \
  --registration-enabled false

# Link Storage DNS zones
for ZONE in blob table queue file; do
  az network private-dns link vnet create \
    --resource-group $RESOURCE_GROUP \
    --zone-name privatelink.${ZONE}.core.windows.net \
    --name link-storage-${ZONE} \
    --virtual-network $VNET_NAME \
    --registration-enabled false
done

Create DNS Records

# Create DNS zone group for Service Bus (auto-registers DNS records)
az network private-endpoint dns-zone-group create \
  --resource-group $RESOURCE_GROUP \
  --endpoint-name pe-servicebus \
  --name default \
  --private-dns-zone privatelink.servicebus.windows.net \
  --zone-name servicebus

# Create DNS zone groups for Storage endpoints
az network private-endpoint dns-zone-group create \
  --resource-group $RESOURCE_GROUP \
  --endpoint-name pe-storage-blob \
  --name default \
  --private-dns-zone privatelink.blob.core.windows.net \
  --zone-name blob

az network private-endpoint dns-zone-group create \
  --resource-group $RESOURCE_GROUP \
  --endpoint-name pe-storage-table \
  --name default \
  --private-dns-zone privatelink.table.core.windows.net \
  --zone-name table

az network private-endpoint dns-zone-group create \
  --resource-group $RESOURCE_GROUP \
  --endpoint-name pe-storage-queue \
  --name default \
  --private-dns-zone privatelink.queue.core.windows.net \
  --zone-name queue

az network private-endpoint dns-zone-group create \
  --resource-group $RESOURCE_GROUP \
  --endpoint-name pe-storage-file \
  --name default \
  --private-dns-zone privatelink.file.core.windows.net \
  --zone-name file

Screenshot: Private DNS zones configured

Step 6: Testing Connectivity

Verify that traffic between the Logic App and backend services stays private.

Verify DNS Resolution

From within the VNet (use a test VM or Cloud Shell connected to the VNet):

# Should resolve to a private IP (10.0.2.x)
nslookup sb-integration-hub.servicebus.windows.net
nslookup stintegrationhub.blob.core.windows.net

Expected output should show the privatelink CNAME and a private IP address:

sb-integration-hub.servicebus.windows.net
  canonical name = sb-integration-hub.privatelink.servicebus.windows.net
  Address: 10.0.2.4

Verify from Logic App

Use the Kudu console (Advanced Tools) on the Logic App:

# From Kudu console (https://<logic-app-name>.scm.azurewebsites.net)
nameresolver sb-integration-hub.servicebus.windows.net
tcpping sb-integration-hub.servicebus.windows.net:443

Screenshot: DNS resolution test from Kudu console

Verify Public Access is Blocked

Attempt to access the Service Bus from outside the VNet — it should be denied:

# From your local machine (should fail with 403)
az servicebus queue list \
  --resource-group $RESOURCE_GROUP \
  --namespace-name $SERVICE_BUS_NAMESPACE

Step 7: Network Security Groups (NSG) Rules

Apply NSGs to control traffic flow within the VNet.

NSG for Integration Subnet

# Create NSG
az network nsg create \
  --resource-group $RESOURCE_GROUP \
  --name nsg-logicapp-integration

# Allow outbound to private endpoints subnet
az network nsg rule create \
  --resource-group $RESOURCE_GROUP \
  --nsg-name nsg-logicapp-integration \
  --name AllowPrivateEndpoints \
  --priority 100 \
  --direction Outbound \
  --access Allow \
  --protocol Tcp \
  --destination-address-prefixes 10.0.2.0/24 \
  --destination-port-ranges 443 5671 5672

# Deny all other outbound internet traffic (optional, strict mode)
az network nsg rule create \
  --resource-group $RESOURCE_GROUP \
  --nsg-name nsg-logicapp-integration \
  --name DenyInternetOutbound \
  --priority 4000 \
  --direction Outbound \
  --access Deny \
  --protocol '*' \
  --destination-address-prefixes Internet \
  --destination-port-ranges '*'

# Associate NSG with integration subnet
az network vnet subnet update \
  --resource-group $RESOURCE_GROUP \
  --vnet-name $VNET_NAME \
  --name snet-logicapp-integration \
  --network-security-group nsg-logicapp-integration

NSG for Private Endpoints Subnet

# Create NSG
az network nsg create \
  --resource-group $RESOURCE_GROUP \
  --name nsg-private-endpoints

# Allow inbound from integration subnet
az network nsg rule create \
  --resource-group $RESOURCE_GROUP \
  --nsg-name nsg-private-endpoints \
  --name AllowFromIntegrationSubnet \
  --priority 100 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes 10.0.1.0/24 \
  --destination-port-ranges 443 5671 5672

# Deny all other inbound
az network nsg rule create \
  --resource-group $RESOURCE_GROUP \
  --nsg-name nsg-private-endpoints \
  --name DenyAllInbound \
  --priority 4000 \
  --direction Inbound \
  --access Deny \
  --protocol '*' \
  --source-address-prefixes '*' \
  --destination-port-ranges '*'

# Associate NSG with private endpoints subnet
az network vnet subnet update \
  --resource-group $RESOURCE_GROUP \
  --vnet-name $VNET_NAME \
  --name snet-private-endpoints \
  --network-security-group nsg-private-endpoints

Screenshot: NSG rules configured

Common Networking Issues and Troubleshooting

Issue: Logic App cannot resolve private endpoint DNS

Symptoms: Workflow fails with connection timeout or DNS resolution errors.

Solutions:

  1. Verify WEBSITE_DNS_SERVER is set to 168.63.129.16 (Azure DNS)
  2. Verify WEBSITE_VNET_ROUTE_ALL is set to 1
  3. Confirm private DNS zones are linked to the VNet
  4. Check DNS zone group is created for the private endpoint

Issue: Logic App cannot connect after VNet integration

Symptoms: All workflows fail, including runtime storage operations.

Solutions:

  1. Ensure storage private endpoints cover all sub-resources (blob, table, queue, file)
  2. Verify the integration subnet has the Microsoft.Web/serverFarms delegation
  3. Check the integration subnet has enough available IP addresses (minimum /27 recommended)
  4. Restart the Logic App after enabling VNet integration

Issue: NSG blocking required traffic

Symptoms: Intermittent failures or specific operations failing.

Solutions:

  1. Check NSG flow logs to identify blocked traffic
  2. Ensure ports 443, 5671, and 5672 are open for Service Bus
  3. Allow Azure Monitor outbound if using Application Insights
  4. Remember that NSG rules on private endpoint subnets require --disable-private-endpoint-network-policies false
# Check effective NSG rules
az network nic list-effective-nsg \
  --resource-group $RESOURCE_GROUP \
  --name <nic-name>

# Enable NSG flow logs for debugging
az network watcher flow-log create \
  --resource-group $RESOURCE_GROUP \
  --name flowlog-integration \
  --nsg nsg-logicapp-integration \
  --storage-account $STORAGE_ACCOUNT \
  --enabled true

Issue: Private endpoint connection stuck in "Pending" state

Solutions:

  1. Approve the private endpoint connection on the target resource
  2. Verify you have sufficient permissions on the target resource
# List pending connections
az network private-endpoint-connection list \
  --id $SB_RESOURCE_ID

# Approve a pending connection
az network private-endpoint-connection approve \
  --id <connection-id>

Issue: Hybrid connectivity (on-premises to Azure)

If connecting to on-premises resources through the VNet:

  1. Ensure VPN Gateway or ExpressRoute is configured
  2. Add routes for on-premises address spaces
  3. Configure DNS forwarding for on-premises DNS zones

Screenshot: Troubleshooting network connectivity

Summary

You have successfully:

  • Created a VNet with dedicated subnets for integration and private endpoints
  • Enabled VNet integration on Logic App Standard
  • Created private endpoints for Service Bus and Storage Account
  • Configured private DNS zones for name resolution
  • Applied NSG rules to control network traffic
  • Verified that all traffic stays within the private network

This configuration ensures your Logic App workflows communicate with backend services entirely over private networks, meeting compliance requirements for regulated industries.


← Previous: CI/CD for Logic Apps | Next: Managed Identity →