You've got a web app that needs to talk to a database on a private endpoint. VNET integration is the answer, but it's not always straightforward.
How It Works
VNET integration gives your App Service outbound access to resources in your VNET. Traffic from your app can reach:
- Private endpoints
- VMs in peered VNETs
- On-premises resources via VPN/ExpressRoute
It does NOT give inbound access - for that you need Private Endpoints for the App Service itself.
Subnet Requirements
The integration subnet has specific requirements:
- Dedicated subnet - Can only be used by App Services
- Delegation - Must be delegated to
Microsoft.Web/serverFarms - Size - Minimum /28, recommended /26 or larger
- No NSG required - But you can add one for extra control
resource "azurerm_subnet" "app_integration" {
name = "snet-app-integration"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = ["10.0.4.0/26"]
delegation {
name = "app-service-delegation"
service_delegation {
name = "Microsoft.Web/serverFarms"
actions = [
"Microsoft.Network/virtualNetworks/subnets/action"
]
}
}
}
Connecting the App Service
resource "azurerm_app_service_virtual_network_swift_connection" "this" {
app_service_id = azurerm_linux_web_app.this.id
subnet_id = azurerm_subnet.app_integration.id
}
Note: This is "Regional VNET Integration" (the modern approach). The old "Gateway-required VNET Integration" is deprecated.
DNS Configuration
VNET integration alone doesn't give you private DNS resolution. Your app will still use public DNS by default.
To resolve private endpoints:
app_settings = {
"WEBSITE_DNS_SERVER" = "168.63.129.16" # Azure DNS
"WEBSITE_VNET_ROUTE_ALL" = "1" # Route all traffic through VNET
}
WEBSITE_VNET_ROUTE_ALL ensures all outbound traffic (not just RFC1918) goes through the VNET, which is necessary for Azure DNS to work.
Private Endpoint DNS Zones
Ensure your private DNS zones are linked to the VNET:
resource "azurerm_private_dns_zone_virtual_network_link" "sql" {
name = "link-sql"
resource_group_name = azurerm_resource_group.this.name
private_dns_zone_name = azurerm_private_dns_zone.sql.name
virtual_network_id = azurerm_virtual_network.this.id
}
Common zones you'll need:
privatelink.database.windows.net- Azure SQLprivatelink.blob.core.windows.net- Blob Storageprivatelink.vaultcore.azure.net- Key Vault
Troubleshooting
Can't reach private endpoint:
- Check VNET integration is connected (Portal → App Service → Networking)
- Verify WEBSITE_VNET_ROUTE_ALL is set
- Check private DNS zone is linked to VNET
- Test from Kudu console:
nameresolver yoursql.database.windows.net
Intermittent connectivity:
- Subnet might be too small. Each App Service instance needs IPs.
- Check if subnet is running out of addresses.
Deployment slots:
- Each slot needs its own VNET integration
- They can share the same subnet (if large enough)
Multiple App Service Plans
If you have multiple App Service Plans, they can share a VNET integration subnet - but each plan needs its own integration resource.
Size your subnet accordingly: each plan instance uses one IP.
Need help with Azure networking? Get in touch - we help organisations design and implement secure cloud architectures.