Cloud
L’intégration VNet pour App Service et Functions n’est pas un Private Endpoint
Une note pratique sur l’intégration VNet d’Azure App Service et Azure Functions, centrée sur l’accès sortant, le DNS, le routage, les NSG, les UDR, le NAT et les erreurs de design fréquentes quand on suppose que l’application devient privée par simple intégration réseau.
L’intégration VNet fait partie des fonctions Azure souvent résumées trop vite. Beaucoup de designs partent d’un besoin légitime, comme joindre une base SQL privée, un Key Vault derrière un private endpoint, ou une API interne accessible seulement via peering ou ExpressRoute. La première étape d’implémentation est souvent correcte elle aussi. L’application est intégrée à un subnet. La confusion commence juste après.
L’intégration VNet ne rend pas votre App Service ou votre Function App joignable en privé depuis le réseau. Elle donne à l’application une capacité d’accès sortant vers un réseau virtuel ou à travers celui-ci. L’exposition entrante est un autre sujet, traité par des fonctions comme Private Endpoint, les access restrictions, ou un App Service Environment selon le design retenu.
Cet article garde volontairement un périmètre serré. Le but n’est pas de décrire toutes les options possibles. Le but est de concevoir proprement une intégration VNet pour App Service et Azure Functions, de comprendre ce que cela change côté routage et DNS, et d’éviter l’erreur classique qui consiste à traiter cette fonction comme un simple interrupteur “mettre l’application dans le VNet”.
Ce que l’article suppose
Les exemples ci-dessous partent d’un App Service multi-tenant ou d’une Function App qui doit accéder en sortie à des ressources privées. Les services cibles peuvent se trouver dans le même VNet, dans un VNet pairé, on premises via ExpressRoute ou VPN, ou derrière des private endpoints.
Le design de référence reste simple.
- Un subnet dédié à l’intégration App Service ou Functions
- Aucune autre ressource déployée dans ce subnet
- Les private endpoints restent sur des subnets séparés
- Le DNS est pensé avant l’intégration de l’application
- Les routes et la politique réseau sont validées avant de forcer tout le trafic sortant dans le VNet
Un petit jeu de variables garde les commandes lisibles.
export RG_APP=rg-app-demo
export RG_NET=rg-network-demo
export LOCATION=westeurope
export APP_NAME=naxaya-web-demo
export PLAN_NAME=asp-naxaya-demo
export FUNC_NAME=naxaya-func-demo
export VNET_NAME=vnet-app-demo
export INTEGRATION_SUBNET=snet-appsvc-integration
export PE_SUBNET=snet-private-endpoints Commencer par la règle de design qui évite le plus d’erreurs
Il faut traiter l’intégration VNet comme un mécanisme de connectivité sortante applicative.
Il faut traiter Private Endpoint comme un mécanisme d’accès entrant privé vers le service.
Si le besoin est “l’application doit joindre une base privée”, l’intégration VNet fait partie de la réponse.
Si le besoin est “des utilisateurs ou clients internes doivent joindre l’application via une adresse privée”, l’intégration VNet ne suffit pas.
La distinction paraît simple, mais c’est précisément là que beaucoup de revues d’architecture se dégradent.
Créer le VNet et garder un subnet d’intégration dédié
Le subnet d’intégration ne doit pas devenir un subnet générique où l’on déploie ensuite tout et n’importe quoi. Il vaut mieux le garder dédié à cette fonction pour que les UDR, les NSG et le dépannage restent lisibles.
az group create -n ${RG_NET} -l ${LOCATION}
az network vnet create -g ${RG_NET} -n ${VNET_NAME} -l ${LOCATION} --address-prefixes 10.42.0.0/16 --subnet-name ${INTEGRATION_SUBNET} --subnet-prefixes 10.42.10.0/27
az network vnet subnet create -g ${RG_NET} --vnet-name ${VNET_NAME} -n ${PE_SUBNET} --address-prefixes 10.42.20.0/27 Il faut valider le subnet avant d’intégrer quoi que ce soit.
az network vnet subnet show -g ${RG_NET} --vnet-name ${VNET_NAME} -n ${INTEGRATION_SUBNET} --query '{name:name,addressPrefix:addressPrefix,delegations:delegations}'
az network vnet subnet show -g ${RG_NET} --vnet-name ${VNET_NAME} -n ${PE_SUBNET} --query '{name:name,addressPrefix:addressPrefix,privateEndpointNetworkPolicies:privateEndpointNetworkPolicies}' Éviter de mélanger dans ce subnet l’intégration VNet, les private endpoints, des jump hosts ou des appliances de sécurité simplement parce qu’il reste des adresses libres. La propreté du rôle du subnet aide directement la qualité du dépannage.
Activer l’intégration VNet sur l’application qui doit réellement sortir vers le privé
On montre d’abord le flux App Service, plus direct. La logique de design reste la même pour une Function App sur un plan compatible.
az group create -n ${RG_APP} -l ${LOCATION}
az appservice plan create -g ${RG_APP} -n ${PLAN_NAME} --sku P1v3 --is-linux
az webapp create -g ${RG_APP} -p ${PLAN_NAME} -n ${APP_NAME} --runtime "NODE|22-lts" On intègre ensuite l’application au subnet dédié.
az webapp vnet-integration add -g ${RG_APP} -n ${APP_NAME} --vnet ${VNET_NAME} --subnet ${INTEGRATION_SUBNET} Il faut contrôler le résultat au lieu de supposer que le blade du portail suffit.
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 'properties.virtualNetworkSubnetId' Si l’application est une Function App, l’intégration VNet reste centrée sur la connectivité sortante. Les détails de support par plan et les autres options réseau diffèrent, mais la question d’architecture reste la même. Quelles ressources privées le code doit-il joindre, et quelle chaîne de routage et de résolution de noms rendra cela prévisible.
Si l’application doit être joignable en privé, il faut le dire explicitement
C’est le point qui devient flou dans beaucoup de schémas.
Une application intégrée au VNet peut joindre des ressources privées. Cela ne veut pas dire qu’un client dans le réseau peut joindre l’application via une adresse privée.
Si le besoin entrant est un accès privé à l’application elle-même, il faut prévoir un Private Endpoint pour l’application ou un autre modèle d’hébergement comme App Service Environment quand la frontière réseau l’exige.
Une phrase utile en revue d’architecture est celle-ci.
“L’intégration VNet règle l’accès privé sortant depuis l’application. Elle ne rend pas à elle seule l’application privée en entrée.”
Le DNS est souvent l’endroit où un design réseau correct échoue quand même
La panne la plus fréquente ne vient pas de l’intégration elle-même. Elle vient de la résolution de noms après intégration.
Exemple, l’application doit joindre un Storage Account, un serveur SQL ou un Key Vault via un private endpoint. Le chemin réseau peut exister et quand même échouer si l’application résout le FQDN public vers une adresse publique ou reçoit une mauvaise réponse privée.
Pour des ressources derrière des private endpoints, il faut d’abord valider les liens des zones DNS privées.
az network private-dns zone create -g ${RG_NET} -n privatelink.database.windows.net
az network private-dns link vnet create -g ${RG_NET} -n link-vnet-app-demo -z privatelink.database.windows.net -v ${VNET_NAME} -e false Si la destination se trouve on premises, il faut vérifier par quel chemin l’application résout les noms après intégration. Si la destination se trouve dans Azure derrière un DNS privé, il faut vérifier les liens de zones, les enregistrements et les éventuels forwarders avant de mettre en cause le routage.
Une vérification très pratique consiste à exposer dans l’application un petit endpoint de diagnostic qui résout le nom cible et tente une connexion, puis à comparer le résultat avec celui d’une VM dans le même VNet. Si la VM résout bien l’adresse privée mais que l’application résout l’adresse publique ou échoue totalement, le problème vient souvent du design DNS plus que du chemin IP.
Décider si seul le trafic applicatif ou tout le trafic sortant doit passer par le VNet
C’est un des choix les plus importants du design.
Le comportement par défaut de l’intégration VNet n’est pas équivalent à “tout le trafic sortant passe désormais dans le VNet”. Azure permet de configurer un routage plus large, et cela change bien plus que les seuls appels applicatifs. Cela peut impacter les pulls d’images, l’accès au contenu partagé, les sauvegardes et l’acquisition de jetons d’identité managée selon la configuration retenue.
Il faut activer le routage complet uniquement quand c’est réellement voulu.
az webapp config set -g ${RG_APP} -n ${APP_NAME} --generic-configurations '{"vnetRouteAllEnabled": true}' Puis il faut le contrôler.
az webapp config show -g ${RG_APP} -n ${APP_NAME} --query '{vnetRouteAllEnabled:vnetRouteAllEnabled}' C’est exactement le point où certaines équipes créent une nouvelle panne après avoir cru améliorer la sécurité. L’application rejoint désormais la base privée, mais le démarrage, le pull d’image, le montage de contenu ou une dépendance de type plan de contrôle deviennent soudain soumis à des NSG ou UDR qui n’ont jamais été testées pour ces flux.
Les NSG et UDR du subnet d’intégration sont de vrais contrôles réseau
Une fois le trafic sortant routé par le VNet, le subnet d’intégration devient une partie directe du chemin opérationnel. Cela signifie que les NSG et les tables de routage doivent refléter une intention réelle.
Un petit exemple d’UDR suffit à montrer le point.
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.42.30.4
az network vnet subnet update -g ${RG_NET} --vnet-name ${VNET_NAME} -n ${INTEGRATION_SUBNET} --route-table rt-appsvc-egress Si ce design est retenu, il faut tester immédiatement la résolution de noms, la joignabilité des cibles, les dépendances Internet encore nécessaires et les flux d’identité managée. Il ne faut pas attendre le prochain déploiement pour découvrir qu’un egress sécurisé a aussi bloqué une partie du comportement de plateforme dont l’application dépendait.
NAT Gateway ne règle pas le même problème que l’intégration VNet
Les équipes veulent souvent une adresse IP publique de sortie prévisible pour des allow lists tierces. Ce besoin n’est pas le même que l’accès privé à des ressources internes.
L’intégration VNet donne à l’application un chemin vers le VNet ou à travers lui. NAT Gateway donne une identité publique de sortie prévisible pour le trafic qui part vers Internet depuis le subnet intégré. Ces fonctions se complètent, mais ne se remplacent pas.
Un rattachement NAT basique ressemble à ceci.
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 --public-ip-addresses pip-appsvc-nat
az network vnet subnet update -g ${RG_NET} --vnet-name ${VNET_NAME} -n ${INTEGRATION_SUBNET} --nat-gateway nat-appsvc Il faut l’utiliser quand le problème porte sur l’identité de sortie. Il ne faut pas l’utiliser comme substitut mental à l’accès privé à des services PaaS Azure ni à la privatisation en entrée de l’application.
Un design réaliste combine souvent intégration VNet et Private Endpoints
Un pattern fréquent et valable ressemble à ceci.
- App Service ou Function App utilise l’intégration VNet sur un subnet dédié
- Les ressources cibles comme SQL, Storage, Key Vault ou des API internes sont privées par design
- Les private endpoints restent sur des subnets séparés
- Les zones DNS privées sont liées au VNet applicatif
- Les NSG, UDR et éventuellement le NAT sont ajoutés ensuite de manière délibérée et testée
C’est souvent ce que les équipes voulaient vraiment dire au départ quand elles disaient “mettre l’application dans le VNet”, sauf que la mécanique réelle repose sur plusieurs fonctions distinctes.
Vérifications après le premier déploiement réussi
Un statut vert dans le portail ne suffit pas. Il faut valider le comportement qui compte.
az webapp vnet-integration list -g ${RG_APP} -n ${APP_NAME}
az webapp config show -g ${RG_APP} -n ${APP_NAME} --query '{vnetRouteAllEnabled:vnetRouteAllEnabled}'
az network vnet subnet show -g ${RG_NET} --vnet-name ${VNET_NAME} -n ${INTEGRATION_SUBNET}
az network private-dns link vnet list -g ${RG_NET} -z privatelink.database.windows.net
az network route-table show -g ${RG_NET} -n rt-appsvc-egress
az network nat gateway show -g ${RG_NET} -n nat-appsvc Puis il faut tester depuis le runtime de l’application lui-même.
- Résoudre le FQDN privé cible depuis le chemin applicatif
- Ouvrir une vraie connexion vers la dépendance privée
- Confirmer que les appels publics sortants fonctionnent encore s’ils restent nécessaires
- Valider l’identité managée ou l’accès aux secrets si le routage a été élargi
- Comparer le comportement avant et après l’application des NSG ou UDR
Si le test est fait uniquement depuis une VM dans le VNet, on peut encore passer à côté d’un problème de DNS ou de routage spécifique au service applicatif.
Les pannes qui apparaissent après le premier statut vert
Les plus courantes sont prévisibles.
L’équipe pense que l’application est désormais privée, alors que l’exposition entrante reste publique car seule l’intégration sortante a été configurée.
L’application est intégrée correctement, mais les ressources privées restent injoignables car la chaîne DNS privée n’a jamais été finalisée.
Le routage complet est activé pour répondre à une exigence sécurité, puis des dépendances de plateforme tombent car des NSG ou UDR ont été écrites trop agressivement.
Le subnet d’intégration est réutilisé pour des ressources sans rapport, ce qui rend l’analyse des routes et des politiques plus confuse qu’elle ne devrait l’être.
Un NAT Gateway est ajouté pour fixer l’IP de sortie, puis traité comme s’il réglait aussi l’accès privé à Azure PaaS.
La revue d’architecture écrit “l’application est dans le VNet” sans distinguer l’intégration sortante de l’accès privé entrant.
Les décisions d’architecture qu’il faut poser noir sur blanc
Avant de standardiser ce pattern, il faut écrire clairement ces choix.
L’application a-t-elle seulement besoin d’un accès privé sortant, ou aussi d’un accès privé entrant.
Seul le trafic applicatif doit-il emprunter le VNet, ou faut-il y forcer tout le trafic sortant.
Quel chemin DNS résout les services privés Azure, et quel chemin résout les noms on premises.
Quels NSG et quelles UDR font partie du design, et lesquels restent des contrôles optionnels à introduire plus tard.
Avez-vous besoin d’une IP publique de sortie prévisible via NAT, ou le vrai problème est-il la joignabilité privée.
App Service ou Functions sur un plan multi-tenant restent-ils adaptés, ou la frontière réseau impose-t-elle App Service Environment.
Quand ces questions ne sont pas documentées, les réponses finissent souvent par être improvisées au fil des incidents.
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, Use Private Endpoints for Apps
- Microsoft Learn, Integrate Azure services with virtual networks for network isolation