VNet Integration¶
Enable VNet integration for a Flask app so outbound traffic to SQL, Redis, and Key Vault flows through private network paths.
flowchart TD
A[Flask app on App Service] --> B[VNet integration subnet]
B --> C[Private Endpoint SQL]
B --> D[Private Endpoint Key Vault]
B --> E[Private Endpoint Redis]
B --> F[NSG and UDR controls] Prerequisites¶
- App Service Plan tier that supports VNet integration
- Existing virtual network with empty delegated subnet for App Service
- Permissions to configure VNet, subnet, NSG, and private endpoints
Main Content¶
1) Create delegated subnet for App Service integration¶
az network vnet subnet create \
--resource-group "$RG" \
--vnet-name "vnet-appservice" \
--name "snet-appservice-integration" \
--address-prefixes "10.10.1.0/24" \
--delegations "Microsoft.Web/serverFarms" \
--output json
2) Connect web app to the integration subnet¶
az webapp vnet-integration add \
--resource-group "$RG" \
--name "$APP_NAME" \
--vnet "vnet-appservice" \
--subnet "snet-appservice-integration" \
--output json
3) Route all outbound traffic through VNet (optional)¶
az webapp config appsettings set \
--resource-group "$RG" \
--name "$APP_NAME" \
--settings WEBSITE_VNET_ROUTE_ALL=1 \
--output json
4) Apply NSG baseline for least-privilege egress¶
Allow required backend ports (for example 1433, 6380, and 443), deny broad outbound, and enable flow logs for troubleshooting.
5) Configure private endpoints and private DNS zones¶
Create private endpoints for SQL, Key Vault, and Redis in a dedicated subnet, then link private DNS zones to the VNet.
Expected zones:
privatelink.database.windows.netprivatelink.vaultcore.azure.netprivatelink.redis.cache.windows.net
6) Set Flask environment-based config on App Service¶
az webapp config appsettings set \
--resource-group "$RG" \
--name "$APP_NAME" \
--settings \
SQL_SERVER_FQDN="<sql-private-fqdn>" \
SQL_DATABASE_NAME="<db-name>" \
REDIS_HOST="<redis-private-fqdn>" \
REDIS_PORT="6380" \
KEY_VAULT_URI="https://<kv-name>.vault.azure.net/" \
--output json
7) Use os.environ plus managed identity in Flask code¶
import os
import pyodbc
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
token = credential.get_token("https://database.windows.net/.default").token
token_bytes = token.encode("utf-16-le")
connection = pyodbc.connect(
"Driver={ODBC Driver 18 for SQL Server};"
f"Server=tcp:{os.environ['SQL_SERVER_FQDN']},1433;"
f"Database={os.environ['SQL_DATABASE_NAME']};"
"Encrypt=yes;TrustServerCertificate=no;",
attrs_before={1256: token_bytes},
)
8) Validate DNS and endpoint state in CI¶
- name: Validate VNet integration and private endpoints
run: |
az webapp vnet-integration list \
--resource-group "$RG" \
--name "$APP_NAME" \
--output table
az network private-endpoint list \
--resource-group "$RG" \
--output table
Inbound versus outbound
VNet integration controls outbound from App Service to private dependencies. It does not place your app inbound endpoint directly inside the VNet.
Verification¶
az webapp vnet-integration listshows expected VNet/subnet mapping.- SQL, Key Vault, and Redis hostnames resolve to private IP addresses.
- Flask app reaches private backends without public network access.
Troubleshooting¶
App cannot reach private backend¶
- Validate NSG and route table configuration for integration subnet.
- Confirm private endpoints are approved and healthy.
- Verify backend firewall and private access policy configuration.
DNS still resolves public addresses¶
- Check private DNS zone links to VNet.
- Validate custom DNS forwarders if you use enterprise DNS.
Connectivity broke after route-all¶
- Verify required Azure destinations remain reachable via your egress path.
- Re-test with route-all disabled to isolate routing policy impact.
Run It in the Portal¶
Portal view: Networking blade (Virtual network integration not yet configured)¶

The Networking blade is the Portal surface where this recipe's outbound VNet connection becomes visible. In the pre-recipe state shown here, Virtual network integration: Not configured is the key row; after az webapp vnet-integration add, this is the row to revisit to confirm the app is attached to the delegated subnet. The Inbound traffic configuration card remains separate, which matches the recipe's point that VNet integration changes outbound connectivity rather than inbound exposure on its own.