Cloud

APIM interne et Azure Function avec Private Endpoint

Concevoir un flux API privé où API Management reste interne et appelle une Azure Function exposée par Private Endpoint, avec DNS privé, séparation des rôles réseau et validations d’exploitation.

14 mai 2026 azureapi-managementazure-functionsprivate-endpointdnsnetworking

Publier une API interne avec Azure API Management et une Azure Function paraît simple sur un schéma. APIM reçoit l’appel, applique ses politiques, puis relaie la requête vers une Function. Le Private Endpoint ferme l’entrée réseau du backend, et l’ensemble semble privé. En exploitation, ce raccourci produit souvent des architectures difficiles à diagnostiquer, parce que la confidentialité réelle du flux dépend surtout du DNS, du mode réseau d’APIM, du plan d’hébergement de la Function et de l’ordre dans lequel l’accès public est fermé.

Le scénario traité ici est celui d’une API métier consommée depuis un réseau interne. Les clients peuvent se trouver dans un spoke Azure, dans un réseau on premises raccordé par ExpressRoute ou VPN, ou derrière un résolveur DNS d’entreprise. API Management reste le point d’entrée API. La Function HTTP porte la logique applicative. Le Private Endpoint expose le backend par une adresse privée. L’objectif n’est pas seulement que le flux fonctionne, mais qu’il reste explicable quand il casse.

Le flux à obtenir

Le flux attendu doit pouvoir être décrit sans ambiguïté. Le client interne résout le nom APIM vers une adresse privée. APIM reçoit l’appel sur son gateway interne, applique les règles d’authentification, de quota, de transformation ou de journalisation, puis appelle la Function avec son nom DNS normal. Ce nom doit se résoudre vers l’adresse privée du Private Endpoint depuis le chemin réseau d’APIM.

text target-flow.txt
Client interne
Résout api.internal.example.com vers l'adresse privée APIM
Appelle l'API publiée par API Management

API Management interne
Reçoit l'appel sur le gateway privé
Applique les politiques API
Résout func-orders-prod.azurewebsites.net vers une adresse privée
Appelle le backend Function avec le hostname attendu

Azure Function
Reçoit l'appel via Private Endpoint
Vérifie l'identité ou le secret attendu côté applicatif
Refuse l'accès public après validation du chemin privé

Cette distinction est importante. APIM dans un VNet ne rend pas automatiquement tous les backends privés. Une Function exposée par Private Endpoint n’est pas automatiquement protégée au niveau applicatif. Le réseau limite les chemins possibles, mais il ne remplace pas une autorisation backend.

Choisir le modèle APIM avant le déploiement

API Management peut être utilisé dans plusieurs modèles réseau. Pour un APIM classique en mode VNet interne, l’instance est injectée dans un subnet dédié et ses endpoints sont accessibles depuis le réseau privé ou depuis les réseaux connectés. Ce modèle est adapté quand APIM doit être un point d’entrée interne et joindre des backends privés dans une architecture hub and spoke.

Le point à éviter est de mélanger les modèles. Un Private Endpoint entrant pour API Management et un APIM injecté en VNet interne ne répondent pas exactement au même besoin. Avant de déployer, il faut décider si l’on veut une instance APIM interne injectée dans un subnet, ou un accès privé au gateway via Private Link dans un autre modèle de service. L’article part volontairement sur un APIM en mode VNet interne, car c’est le cas le plus lisible pour une API strictement interne.

text apim-model.txt
Décision retenue
API Management en mode VNet interne
Gateway exposé uniquement sur le réseau privé
Backend Azure Function joint par son hostname normal
Résolution privée du backend via Private DNS

Décision non retenue dans ce pattern
Backend appelé par adresse IP privée
Function ouverte publiquement pour simplifier les tests
Authentification backend remplacée par le seul Private Endpoint

Séparer les subnets pour garder un diagnostic lisible

Le subnet APIM doit rester dédié à APIM. Le subnet Private Endpoint porte les interfaces privées des services PaaS, dont la Function. Si la Function doit elle-même sortir vers SQL, Storage, Key Vault ou une API interne privée, un subnet d’intégration VNet séparé peut être nécessaire. Cette séparation évite de confondre entrée privée et sortie applicative.

bash 01-network.sh
export RG_NET=rg-net-prod
export LOCATION=westeurope
export VNET_NAME=vnet-prod-api
export APIM_SUBNET=snet-apim-internal
export PE_SUBNET=snet-private-endpoints
export FUNC_INTEGRATION_SUBNET=snet-function-integration

az group create -n $RG_NET -l $LOCATION
az network vnet create -g $RG_NET -n $VNET_NAME -l $LOCATION --address-prefixes 10.80.0.0/16
az network vnet subnet create -g $RG_NET --vnet-name $VNET_NAME -n $APIM_SUBNET --address-prefixes 10.80.10.0/26
az network vnet subnet create -g $RG_NET --vnet-name $VNET_NAME -n $PE_SUBNET --address-prefixes 10.80.20.0/27
az network vnet subnet create -g $RG_NET --vnet-name $VNET_NAME -n $FUNC_INTEGRATION_SUBNET --address-prefixes 10.80.30.0/27 --delegations Microsoft.Web/serverFarms

La délégation du subnet d’intégration Function ne doit pas être appliquée au subnet APIM. Le subnet Private Endpoint ne doit pas être utilisé comme subnet applicatif générique. Ces détails évitent beaucoup de confusion lorsque les routes, les NSG ou les résolutions DNS doivent être comparées.

Rendre le DNS explicite

Le DNS est souvent le vrai point de bascule entre une architecture privée et une architecture seulement partiellement privée. Les clients doivent résoudre le gateway APIM vers son adresse privée. APIM doit résoudre le backend Function vers l’adresse privée du Private Endpoint. Les deux sujets sont séparés et doivent être validés séparément.

text dns-records.txt
Zone interne de l'entreprise
api.internal.example.com          A      adresse privée APIM
portal.internal.example.com       A      adresse privée APIM
management.internal.example.com   A      adresse privée APIM

Zone Azure Private DNS
privatelink.azurewebsites.net     liée au VNet utilisé par APIM
func-orders-prod                  A      adresse privée du Private Endpoint
func-orders-prod.scm              A      adresse privée SCM si le déploiement privé est requis

Un test sur adresse IP ne suffit pas. Il faut tester le nom réellement utilisé par les clients et le nom réellement utilisé par APIM pour appeler la Function. Sinon, on ne valide ni le certificat TLS, ni le host header, ni la résolution qui sera utilisée en production.

Créer le Private Endpoint de la Function

La Function doit être hébergée sur un plan compatible avec Private Endpoint. Le Private Endpoint se crée sur le sous-resource sites. Le nom public func-orders-prod.azurewebsites.net continue d’exister, mais il doit résoudre vers privatelink.azurewebsites.net puis vers une adresse privée depuis les réseaux concernés.

bash 02-function-private-endpoint.sh
export RG_APP=rg-api-prod
export FUNC_NAME=func-orders-prod
export PE_FUNC_NAME=pe-func-orders-prod
export DNS_ZONE_FUNC=privatelink.azurewebsites.net

FUNC_ID=$(az functionapp show -g $RG_APP -n $FUNC_NAME --query id -o tsv)
az network private-dns zone create -g $RG_NET -n $DNS_ZONE_FUNC
az network private-dns link vnet create -g $RG_NET -n link-$VNET_NAME-azurewebsites -z $DNS_ZONE_FUNC -v $VNET_NAME -e false
az network private-endpoint create -g $RG_NET -n $PE_FUNC_NAME -l $LOCATION --vnet-name $VNET_NAME --subnet $PE_SUBNET --private-connection-resource-id $FUNC_ID --group-id sites --connection-name cn-$PE_FUNC_NAME
az network private-endpoint dns-zone-group create -g $RG_NET --endpoint-name $PE_FUNC_NAME -n dzg-$PE_FUNC_NAME --private-dns-zone $DNS_ZONE_FUNC --zone-name $DNS_ZONE_FUNC

La validation doit être faite depuis un hôte qui utilise le même DNS que le chemin APIM. Le résultat attendu est une adresse du subnet Private Endpoint. Un simple code HTTP ne suffit pas, car une erreur 401, 403 ou 404 peut être normale selon l’authentification ou la route applicative.

bash 03-dns-validation.sh
nslookup $FUNC_NAME.azurewebsites.net
nslookup $FUNC_NAME.privatelink.azurewebsites.net
curl -I https://$FUNC_NAME.azurewebsites.net/api/health

Configurer APIM sans contourner TLS

APIM doit appeler la Function avec son hostname, pas avec l’adresse IP privée du Private Endpoint. L’adresse IP paraît pratique pendant un test, mais elle casse le modèle TLS et rend la configuration fragile. Le backend APIM doit donc rester de la forme https://func-orders-prod.azurewebsites.net/api.

xml apim-backend-policy.xml
<policies>
<inbound>
  <base />
  <set-backend-service base-url="https://func-orders-prod.azurewebsites.net/api" />
  <set-header name="x-correlation-id" exists-action="override">
    <value>@(context.RequestId.ToString())</value>
  </set-header>
</inbound>
<backend>
  <base />
</backend>
<outbound>
  <base />
</outbound>
<on-error>
  <base />
</on-error>
</policies>

L’authentification backend doit être explicite. Selon le contexte, APIM peut transmettre une clé Function, présenter un certificat client, obtenir un jeton Entra ID ou injecter un secret depuis Key Vault. Le choix dépend du niveau de sécurité attendu, mais il ne doit pas être remplacé par le seul Private Endpoint.

Fermer l’accès public après preuve du chemin privé

L’accès public de la Function doit être fermé après validation du chemin privé. Fermer trop tôt complique les diagnostics, car il devient difficile de distinguer un problème DNS, un problème de routage, une erreur TLS, une erreur d’authentification et un problème applicatif.

bash 04-close-public-access.sh
az functionapp update -g $RG_APP -n $FUNC_NAME --set publicNetworkAccess=Disabled
az functionapp show -g $RG_APP -n $FUNC_NAME --query '{name:name, publicNetworkAccess:publicNetworkAccess, defaultHostName:defaultHostName}'

Après fermeture, il faut rejouer le même test depuis APIM et depuis un réseau qui ne doit pas accéder au backend. Le premier doit fonctionner. Le second doit échouer. Si les deux fonctionnent encore, le flux public n’a pas réellement été fermé ou un autre chemin existe.

Diagnostiquer les pannes récurrentes

Les erreurs les plus fréquentes sont prévisibles. La zone privatelink.azurewebsites.net existe mais n’est pas liée au bon VNet. APIM appelle encore une URL backend publique. Un DNS on premises ne forwarde pas le suffixe privé vers Azure. Une IP privée a été placée dans l’URL backend et TLS échoue. Le public network access a été désactivé avant que les agents de déploiement aient un chemin vers SCM. Le stockage de la Function reste public alors que l’entrée HTTP a été privatisée.

text troubleshooting.txt
APIM retourne 500 ou 502
Vérifier la résolution DNS du backend depuis le même chemin que APIM
Vérifier que l'URL backend utilise le hostname attendu
Vérifier l'authentification backend
Lire les logs APIM et Application Insights avec le même correlation ID

La Function répond depuis une VM mais pas depuis APIM
Comparer les résolveurs DNS utilisés
Vérifier les liens de Private DNS Zone
Vérifier les NSG et les routes du subnet APIM
Vérifier que la politique APIM ne réécrit pas le backend

Tout casse après fermeture de l'accès public
Prouver que le flux passait réellement par Private Endpoint avant fermeture
Vérifier SCM et les agents de déploiement
Vérifier les dépendances Storage, Key Vault et identité managée

Conclusion

APIM interne et Azure Function avec Private Endpoint forment un pattern fiable quand chaque composant garde son rôle. APIM publie et gouverne l’API. Le Private Endpoint ferme l’entrée réseau vers le backend. La Function vérifie l’appel attendu. Le DNS prouve que le flux passe réellement par le réseau privé.

Le test final doit être opérationnel. Depuis un client interne, il faut résoudre APIM, appeler l’API, suivre la corrélation dans APIM, vérifier que le backend Function est résolu en privé, confirmer l’exécution côté Function, puis rejouer exactement le même scénario après désactivation de l’accès public. Si cette chaîne tient sans hosts file, sans IP codée en dur et sans exception temporaire conservée en production, le design est exploitable.