Cloud

Azure App Service et Functions : VNet Integration n'est pas un Private Endpoint

Concevoir la VNet Integration pour App Service et Azure Functions en séparant sortie applicative, DNS privé, Route All, NSG, UDR, NAT Gateway et accès entrant privé.

15 juin 2026 azureapp-servicefunctionsvnetnetworkingdnsnat-gatewayudrnsgrunbook

La VNet Integration Azure App Service et Azure Functions est souvent ajoutée au bon moment, mais pour une mauvaise raison. Une application doit joindre une base SQL privée, un Key Vault derrière Private Link, une API interne dans un spoke, ou un service on premises via ExpressRoute. L’équipe intègre alors l’application à un subnet, constate un statut vert dans le portail, puis suppose que l’application est maintenant « dans le VNet ».

C’est ce raccourci qui crée les incidents. La VNet Integration donne au runtime un chemin de sortie vers ou à travers un Virtual Network. Elle ne rend pas l’App Service ou la Function joignable en entrée par une adresse privée. L’accès entrant privé relève d’un Private Endpoint, d’un App Service Environment, d’API Management, d’Application Gateway ou d’un autre choix d’exposition.

Le scénario fil rouge est une application app-prod-orders et une Function func-prod-billing qui doivent appeler SQL, Storage, Key Vault et une API interne sans ouvrir ces dépendances publiquement. L’objectif n’est pas seulement que cela fonctionne, mais que DNS, routage, sécurité réseau et rollback restent lisibles en exploitation.

Commencer par la bonne séparation

La phrase de cadrage à écrire dans le design est simple.

text vnet-integration-design-rule.txt
VNet Integration
Sert à faire sortir l'application vers un VNet
Permet d'atteindre des ressources privées, des VNets peerés ou un réseau on premises
Se diagnostique avec DNS, routes, NSG, UDR, NAT et dépendances de plateforme

Private Endpoint sur App Service ou Function
Sert à faire entrer les clients vers l'application par Private Link
Ne transporte pas la sortie applicative
Se diagnostique avec DNS d'entrée, accès public, clients, gateway et logs HTTP

NAT Gateway
Sert à stabiliser l'IP publique de sortie quand le trafic sort vers Internet
Ne remplace ni Private Endpoint, ni DNS privé, ni autorisation applicative

Cette séparation évite de mélanger trois questions différentes : qui peut appeler l’application, ce que l’application peut appeler, et par quelle IP publique elle sort quand elle doit encore joindre Internet.

Garder les subnets spécialisés

Un design exploitable sépare les rôles réseau. Le subnet d’intégration App Service ou Functions porte la sortie des applications. Les Private Endpoints des services PaaS restent dans un autre subnet. Les appliances, bastions et workloads classiques ne doivent pas être ajoutés dans le subnet d’intégration juste parce qu’il reste des adresses.

bash 01-network-layout.sh
export RG_NET=rg-prod-network
export LOCATION=westeurope
export VNET_NAME=vnet-prod-app
export APP_INTEGRATION_SUBNET=snet-appsvc-integration
export FUNC_INTEGRATION_SUBNET=snet-function-integration
export PE_SUBNET=snet-private-endpoints

az group create -n $RG_NET -l $LOCATION

az network vnet create -g $RG_NET -n $VNET_NAME -l $LOCATION --address-prefixes 10.70.0.0/16

az network vnet subnet create -g $RG_NET --vnet-name $VNET_NAME -n $APP_INTEGRATION_SUBNET --address-prefixes 10.70.10.0/27 --delegations Microsoft.Web/serverFarms

az network vnet subnet create -g $RG_NET --vnet-name $VNET_NAME -n $FUNC_INTEGRATION_SUBNET --address-prefixes 10.70.11.0/27 --delegations Microsoft.Web/serverFarms

az network vnet subnet create -g $RG_NET --vnet-name $VNET_NAME -n $PE_SUBNET --address-prefixes 10.70.20.0/27

La séparation ne sert pas l’esthétique du schéma. Elle permet de savoir quelle table de routes, quel NSG et quel lien DNS impactent une panne donnée. Si app-prod-orders ne joint plus SQL, l’équipe doit regarder le subnet d’intégration, pas le subnet Private Endpoint de l’App Service.

Activer la VNet Integration et vérifier l’état réel

L’intégration doit être validée côté ressource, pas seulement dans le portail. Les commandes suivantes gardent l’exemple lisible avec App Service ; la même logique s’applique aux Function Apps sur plans compatibles.

bash 02-enable-vnet-integration.sh
export RG_APP=rg-prod-app
export APP_NAME=app-prod-orders
export FUNC_NAME=func-prod-billing

az webapp vnet-integration add -g $RG_APP -n $APP_NAME --vnet $VNET_NAME --subnet $APP_INTEGRATION_SUBNET

az webapp vnet-integration list -g $RG_APP -n $APP_NAME

az resource show -g $RG_APP -n $APP_NAME --resource-type Microsoft.Web/sites --query '{name:name, integrationSubnet:properties.virtualNetworkSubnetId}'

Le point important est de rattacher chaque application au subnet prévu. Une Function trigger qui consomme Service Bus, une API web et un worker batch n’ont pas forcément le même profil de sortie. Le subnet choisi devient un élément du runbook.

Traiter DNS avant de traiter les routes

La plupart des erreurs de VNet Integration ressemblent d’abord à des erreurs réseau. En pratique, beaucoup commencent par DNS. L’application peut avoir un chemin vers le VNet et continuer à résoudre sql-prod-orders.database.windows.net ou kv-prod-orders.vault.azure.net vers une cible publique, ou ne pas résoudre le nom on premises attendu.

text dns-decision-table.txt
Destination appelee par l'application
Azure SQL avec Private Endpoint
  Zone attendue: privatelink.database.windows.net
  Validation: le FQDN public suit la chaine privatelink et retourne une IP privee

Key Vault avec Private Endpoint
  Zone attendue: privatelink.vaultcore.azure.net
  Validation: l'identite est autorisee et le nom se resout depuis le runtime

Storage prive
  Zone attendue selon le sous-service: blob, file, queue ou table
  Validation: le bon sous-resource est prive, pas seulement le compte Storage

API interne dans un spoke
  Zone attendue: DNS interne de l'entreprise ou Azure Private DNS
  Validation: resolution identique depuis le runtime et depuis un point de test comparable

Systeme on premises
  Zone attendue: forwarding DNS vers les resolvers d'entreprise
  Validation: ExpressRoute/VPN ne suffit pas si le nom ne se resout pas

Un test depuis une VM du VNet aide, mais il ne remplace pas un test depuis le runtime applicatif. Une VM peut utiliser un résolveur différent, ne pas subir les mêmes routes, et donner un faux sentiment de validation.

Décider ce qui doit passer par le VNet

La question suivante est souvent mal formulée. Il ne faut pas seulement demander « l’application est-elle intégrée au VNet ? ». Il faut demander quels flux sortants passent par cette intégration.

Selon le besoin, l’équipe peut router uniquement le trafic applicatif vers les plages privées, ou forcer une part plus large du trafic de configuration et de sortie à travers le VNet. Ce choix a des effets sur les dépendances de plateforme : image de conteneur, content share, backup/restore, acquisition de jeton managed identity et accès à Internet.

bash 03-route-through-vnet.sh
APP_ID=$(az webapp show -g $RG_APP -n $APP_NAME --query id -o tsv)

az resource update --ids $APP_ID --set properties.outboundVnetRouting.applicationTraffic=true

az resource update --ids $APP_ID --set properties.outboundVnetRouting.allTraffic=true

az resource show --ids $APP_ID --query '{applicationTraffic:properties.outboundVnetRouting.applicationTraffic, allTraffic:properties.outboundVnetRouting.allTraffic}'

allTraffic=true ne doit pas être activé comme réflexe de durcissement. Il faut d’abord savoir où passent les images, le stockage de contenu, les appels d’identité managée, les dépendances externes et les flux de supervision. Sinon, la correction d’un accès SQL privé peut casser un démarrage de conteneur ou un montage de contenu.

Utiliser NSG et UDR comme des contrôles de production

Une fois le trafic applicatif routé par le VNet, les NSG et UDR du subnet d’intégration deviennent des composants actifs du chemin. Ils ne sont pas de simples garde-fous décoratifs.

bash 04-egress-routing.sh
az network route-table create -g $RG_NET -n rt-appsvc-egress

az network route-table route create -g $RG_NET --route-table-name rt-appsvc-egress -n default-to-firewall --address-prefix 0.0.0.0/0 --next-hop-type VirtualAppliance --next-hop-ip-address 10.70.30.4

az network vnet subnet update -g $RG_NET --vnet-name $VNET_NAME -n $APP_INTEGRATION_SUBNET --route-table rt-appsvc-egress

Après ce changement, la validation doit couvrir au minimum quatre flux : DNS, dépendance privée cible, dépendance Internet encore nécessaire, et identité managée. Un test qui ne vérifie que SQL peut laisser une panne latente sur Key Vault, ACR, Storage ou une API de paiement externe.

Ajouter NAT Gateway uniquement pour le besoin d’egress public

NAT Gateway répond à une demande précise : stabiliser les IP publiques de sortie pour des services tiers qui font de l’allow list. Ce n’est pas un mécanisme d’entrée privée, ni un substitut à Private DNS.

bash 05-nat-gateway.sh
az network public-ip create -g $RG_NET -n pip-appsvc-nat --sku Standard

az network nat gateway create -g $RG_NET -n nat-appsvc-egress --public-ip-addresses pip-appsvc-nat

az network vnet subnet update -g $RG_NET --vnet-name $VNET_NAME -n $APP_INTEGRATION_SUBNET --nat-gateway nat-appsvc-egress

Le runbook doit documenter pourquoi NAT existe. Si la raison est « IP fixe pour un partenaire externe », la preuve attendue est l’IP vue par ce partenaire. Si la raison est « accès privé à SQL », NAT n’est pas la bonne réponse.

Construire une validation depuis l’application

Le test le plus utile est un diagnostic minimal exécuté depuis le chemin réel de l’application. Il peut prendre la forme d’un endpoint protégé, d’une commande Kudu/console, d’un job temporaire ou d’un worker de validation supprimé après usage.

text runtime-validation-card.txt
Validation VNet Integration - app-prod-orders

Horodatage
2026-06-15T20:00:00Z

Depuis le runtime
Resolve sql-prod-orders.database.windows.net
Resolve kv-prod-orders.vault.azure.net
Resolve api-pricing.internal.example.com

Connexions reelles
SQL: ouverture TCP puis requete simple
Key Vault: lecture d'un secret de test avec managed identity
API interne: GET /health avec correlation ID
Internet autorise: appel vers endpoint externe attendu si necessaire

Preuves a joindre au ticket
IP resolues
Codes de retour
Correlation ID
Extrait de logs applicatifs
Etat des routes, NSG, NAT et outboundVnetRouting

Cette carte force le diagnostic à rester concret. La question n’est pas « le réseau est-il sécurisé ? », mais « le runtime résout-il et joint-il les dépendances prévues par le chemin prévu ? ».

Les pannes typiques après un statut vert

Les incidents récurrents ne sont pas mystérieux. L’application est bien intégrée au VNet, mais la zone Private DNS n’est pas liée au bon VNet. allTraffic est activé et une UDR envoie tout vers un firewall qui ne connaît pas les flux de plateforme. Le subnet d’intégration est réutilisé pour des ressources qui n’ont rien à y faire. NAT Gateway est ajouté pour une allow list externe, puis l’équipe pense à tort que les dépendances Azure privées sont couvertes. Un Private Endpoint est créé sur l’App Service, mais les appels sortants vers SQL passent toujours par le chemin public.

Le diagnostic doit donc revenir à la séparation initiale : sortie applicative, entrée privée, DNS, routes, sécurité réseau, identité et dépendance réellement appelée.

Décider du rollback minimal

La VNet Integration touche plusieurs couches. Le rollback doit viser la couche changée, pas annuler toute la livraison.

text vnet-integration-rollback.txt
Changement recent
Lien Private DNS ou forwarder
  Rollback: restaurer le lien VNet ou la regle de forwarding precedente
  Preuve: resolution privee depuis le runtime

outboundVnetRouting ou Route All
  Rollback: revenir au routage precedent
  Preuve: dependance cible et dependances plateforme revalidees

NSG ou UDR sur subnet d'integration
  Rollback: restaurer les regles ou routes precedentes
  Preuve: DNS, identite, dependance privee et egress public attendu fonctionnent

NAT Gateway
  Rollback: detacher NAT du subnet si l'egress public n'est plus conforme
  Preuve: IP de sortie observee et impact partenaire documente

Private Endpoint entrant sur l'application
  Rollback: traiter comme un sujet d'exposition entrante, pas comme un probleme de VNet Integration
  Preuve: clients internes, DNS d'entree et logs HTTP valides

Conclusion

La VNet Integration est un outil d’architecture très utile, mais seulement si son rôle reste clair. Elle donne à App Service ou Azure Functions un chemin de sortie contrôlé vers un VNet. Elle doit être pensée avec DNS, routes, NSG, UDR, NAT Gateway, identité managée et dépendances de plateforme.

Private Endpoint garde sa place pour l’entrée privée ou pour les dépendances PaaS appelées par l’application. Mais il ne doit pas absorber tout le raisonnement réseau. Un design Azure privé exploitable sépare les sens de trafic, documente les décisions de routage, et valide depuis le runtime avant de fermer, forcer ou rediriger les flux.

Références

  • Microsoft Learn, Integrate your app with an Azure virtual network
  • Microsoft Learn, Enable virtual network integration in Azure App Service
  • Microsoft Learn, Manage Azure App Service virtual network integration routing
  • Microsoft Learn, Azure Functions networking options
  • Microsoft Learn, Configure Azure NAT Gateway Integration
  • Microsoft Learn, Use private endpoints for Azure App Service apps