Acceder a un App Service con Private Endpoint desde otra Vnet

En la segunda parte de esta serie de artículos sobre conectividad en App Service a través de Private Endpoint hoy toca otro escenario muy común donde lo que queremos es acceder a la web app desde otro recurso que no se encuentra en la misma red que esta:

Acceso a la Web App desde una VM en otra red

Este artículo forma parte de una serie donde ya hemos visto:

  1. Acceder a un App Service con Private Endpoint dentro de la misma VNET.

Para poder montar este entorno necesitas los pasos del primero.

Crear other-vnet y other-vnet-vm

Partiendo de que ya tienes la red webapp-vnet y el App Service con Private Endpoint configuradas, lo siguiente que vamos a hacer es crear otra red llamada other-vnet con una máquina virtual en ella:

###### Scenario 2: Access a web app from a VM in a different VNET ######

# 11. Create a new resource group
OTHER_RESOURCE_GROUP="Other-VM-In-Another-VNet"
az group create -n $OTHER_RESOURCE_GROUP --location $LOCATION

# 12. Create a new VNET
OTHER_VNET_NAME="other-vnet"
OTHER_VNET_CIDR=10.20.0.0/16
OTHER_SUBNET_NAME="other-vms"
OTHER_SUBNET_CIDR=10.20.1.0/24

# 13. Create other VNET
az network vnet create \
--name $OTHER_VNET_NAME \
--resource-group $OTHER_RESOURCE_GROUP \
--location $LOCATION \
--address-prefixes $OTHER_VNET_CIDR \
--subnet-name $OTHER_SUBNET_NAME \
--subnet-prefixes $OTHER_SUBNET_CIDR

# 14. Create other VM in the other-vnet
az vm create \
--name $OTHER_VNET_NAME \
--resource-group $OTHER_RESOURCE_GROUP \
--vnet-name $OTHER_VNET_NAME \
--subnet $OTHER_SUBNET_NAME \
--image "Win2019Datacenter" \
--admin-username "azureuser" \
--admin-password "[email protected]" \
--nsg-rule NONE

# 15. Create a bastion host
BASTION_PUBLIC_IP_NAME="bastion-for-other-vnet-public-ip"
BASTION_HOST_NAME="bastion-for-other-vnet-host"
BASTION_SUBNET_CIDR=10.20.2.0/27

# 16 Create a subnet for the bastion host
az network vnet subnet create \
--name AzureBastionSubnet \
--resource-group $OTHER_RESOURCE_GROUP \
--vnet-name $OTHER_VNET_NAME \
--address-prefixes $BASTION_SUBNET_CIDR

# 17 Create a public IP
az network public-ip create \
--resource-group $OTHER_RESOURCE_GROUP \
--name $BASTION_PUBLIC_IP_NAME \
--sku Standard --location $LOCATION

# 18 Create a bastion host
az network bastion create --name $BASTION_HOST_NAME \
--resource-group $OTHER_RESOURCE_GROUP \
--location $LOCATION \
--vnet-name $OTHER_VNET_NAME \
--public-ip-address $BASTION_PUBLIC_IP_NAME

Para que se vean claros los recursos generados para este escenario, he creado un nuevo grupo de recursos llamado Other-VM-In-Another-VNet, creo que bastante descriptivo 🙂 , donde deberías de ver lo siguiente:

Recursos creados en Other-VM-In-Another-VNet

No hace falta probar que si ahora mismo accedo, a través de Azure Bastión, a la nueva máquina virtual e intento acceder a https://internalweb.azurewebsites.net el resultado será un 403, ya que:

  1. No tiene ningún tipo de conexión con la red donde está la webapp, esto es webapp-vnet.
  2. Esta red todavía no conoce de la existencia del servidor DNS, en este caso un Private DNS Zone, por lo que no sabe resolver internamente esta dirección.

Es por ello que tenemos dos puntos que resolver.

1. Conectar las dos redes entre sí

Para poder hacer esto posible tiene que entrar en juego lo que se conoce como peerings (emparejamientos). Esto permite que recursos de dos VNET puedan hablarse entre sí, como si estuvieran en la misma, incluso aunque los rangos de IPs sean distintos, mientras que estos no se solapen (es decir, que estés usando justamente el mismo rango en ambas VNETs).

Peering entre other-vnet y webapp-vnet

Para crear este peering entre ellas es necesario lanzar dos comandos, aunque en el portal parece como si fuera uno solo:

# 19. Create a peering between webapp and other-vnet. It has to be in both directions.
WEB_APP_VNET_ID=$(az network vnet show --name $WEB_APP_VNET_NAME --resource-group $WEB_APP_RESOURCE_GROUP --query id --output tsv)

az network vnet peering create \
--name "peering-with-webapp-vnet" \
--resource-group $OTHER_RESOURCE_GROUP \
--vnet-name $OTHER_VNET_NAME \
--remote-vnet $WEB_APP_VNET_ID \
--allow-vnet-access \
--allow-forwarded-traffic

OTHER_VNET_ID=$(az network vnet show --name $OTHER_VNET_NAME --resource-group $OTHER_RESOURCE_GROUP --query id --output tsv)

az network vnet peering create \
--name "peering-with-other-vnet" \
--resource-group $WEB_APP_RESOURCE_GROUP \
--vnet-name $WEB_APP_VNET_NAME \
--remote-vnet $OTHER_VNET_ID \
--allow-vnet-access \
--allow-forwarded-traffic

Esto es así porque es necesario aprobar la comunicación en ambas redes. Si solo creas el peering en una de las dos VNET (esto lo puedes ver si solo ejecutas el primer comando), el estado quedará como Initiated, pero no Connected:

Peering iniciado pero no completado entre other-vnet y webapp-vnet

Por lo tanto, para que la comunicación entre ambas redes sea posible debe estar dada de alta en los dos extremos:

Peering completado entre las dos vnets

Esto tiene sentido porque debo tener acceso a ambas redes para poder hacer esta conexión. Si lo haces desde el portal y tienes acceso lo harás todo en un solo paso.

Ahora podrías creer que ya es posible la comunicación entre other-vnet-vm e internalweb.azurewebsites.net, pero la realidad es que si vuelves a probar, aun con el peering habilitado entre ambas redes, seguimos obteniendo la misma respuesta: 403.

Intento de acceso a internalweb desde other-vnet-vm a través del hostname sin Private DNS Zone configurado

Sin embargo, si intentas acceder a través de la IP interna de la web, dentro de webapp-vnet, verás que recibes un 400 (Bad request).

Intento de acceso a internalweb desde other-vnet-vm a través de la IP – Error 400 (Bad Request)

Esto es buena señal porque significa que realmente estás llegando a la web pero no puedes hacerlo a través de la IP, como explicaba en el artículo anterior.

2. Asociar la nueva red a tu Private DNS Zone

Para finalizar este escenario nos queda un paso más y es que esta nueva VNET también utilice nuestro Private DNS Zone para resolver el nombre de la web:

Asociar other-vnet con el Private DNS Zone pra poder resolver el nombre del Private Endpoint

Para ello, basta con lanzar el mismo comando de asociación entre el Private DNS Zone y esta vez con la vnet other-vnet:

# 20. Link between other-vnet and the Private DNS Zone
az network private-dns link vnet create \
--name "${OTHER_VNET_NAME}-link" \
--resource-group $WEB_APP_RESOURCE_GROUP \
--registration-enabled false \
--virtual-network $OTHER_VNET_ID \
--zone-name privatelink.azurewebsites.net

Si ahora accedieras al recurso Private DNS Zone en el grupo de recursos de la web app, Web-App-With-Private-Endpoint, verías en el apartado Virtual Network links que ahora tienes las dos redes enlazadas:

Private DNS Zones con las dos redes enlazadas

Si ahora compruebas el acceso a través de other-vnet-vm funcionará correctamente.

Intento de acceso a internalweb desde other-vnet-vm a través del hostname con Private DNS Zone configurado

Los comandos los tienes en mi GitHub.

¡Saludos!