Cuando incrementas la seguridad en tus App Services, añadiéndoles un private link, pierdes la capacidad de usar de manera regular el despliegue desde tus herramientas automatizadas, ya que los agentes gestionados por el proveedor no son capaces de llegar a tu web desde Internet. De la misma forma que no puedes desplegar desde tu local o cualquier otro sitio que no tenga comunicación con la red interna. Normalmente, la solución pasa por tener un agente o máquina en la red interna, que sí que tenga acceso a tu sitio y desde ahí hacer el despliegue. Hoy quiero contarte una alternativa que te evita tener esa máquina adicional.
Extensión Onedeploy
Dentro de la API REST https://management.azure.com existe una llamada a la extensión onedeploy, que te permite decirle a Kudu que utilice un paquete remoto para hacer el despliegue. De tal forma que yo no soy quien le intenta enviar el paquete a este para que lo despliegue, lo cual daría error porque desde Internet no tengo acceso, sino que la acción es la inversa y es Kudu quien se va a buscarlo 😃
Cómo usarlo
A día de hoy esta llamada no está disponible a través de Azure CLI, pero es posible invocarla de manera sencilla. Este sería un ejemplo con el código del repositorio 0GiS0/todo-sample-for-app-svc, que es una aplicación en .NET 6 que depende de una base de datos SQL Server. Si no tienes un entorno para probarlo puedes seguir este artículo, donde tienes un paso a paso de cómo crear un App Service con private link y una máquina de salto dentro de la misma red. Una vez que tengas tu sitio privado, lo siguiente que necesitas es tener una cuenta de almacenamiento de Azure:
# Create a Storage Account
STORAGE_ACCOUNT_NAME="fordeploys"
az storage account create \
--name $STORAGE_ACCOUNT_NAME \
--resource-group $RESOURCE_GROUP \
--location $LOCATION \
--sku Standard_LRS
Clona el contenido del repositorio, o del que tu elijas, genera los binarios y haz un zip con el resultado:
git clone https://github.com/0GiS0/todo-sample-for-app-svc.git && cd todo-sample-for-app-svc
dotnet build --configuration Release
dotnet publish -c Release -o dotnetapp/
cd dotnetapp
zip -r dotnetapp.zip .
Este será el paquete que vas a desplegar, que normalmente lanzarías desde Azure CLI. En este caso vamos a subir el paquete a un contenedor de nuestra cuenta de almacenamiento:
# Create a container
az storage container create \
--name packages \
--account-name $STORAGE_ACCOUNT_NAME
# Upload the file
az storage blob upload \
--account-name $STORAGE_ACCOUNT_NAME \
--container-name packages \
--name dotnetapp.zip \
--file dotnetapp.zip
Al ser un contenedor privado necesitas crear además una URL accesible durante un periodo corto de tiempo:
# Create a sas token
STORAGE_ACCOUNT_KEY=$(az storage account keys list --account-name $STORAGE_ACCOUNT_NAME --resource-group $RESOURCE_GROUP --query "[0].value" --output tsv)
end=`date -v+30M '+%Y-%m-%dT%H:%MZ'`
SAS=$(az storage account generate-sas --permissions rl --account-name $STORAGE_ACCOUNT_NAME --account-key $STORAGE_ACCOUNT_KEY --services b --resource-types co --expiry $end -o tsv)
ZIP_URL="https://$STORAGE_ACCOUNT_NAME.blob.core.windows.net/packages/dotnetapp.zip?$SAS"
Y ahora ya tenemos todo lo que necesitamos para llevar a cabo la llamada. La misma está compuesta de los siguientes elementos:
SITE_URI="https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Web/sites/${WEBAPP_NAME}/extensions/onedeploy?api-version=2020-12-01"
Necesitas el id de la suscripción donde está tu sitio, el grupo de recursos y el nombre de la web app. En el caso de que estés utilizando Deployment slots la misma cambia ligeramente:
SITE_URI="https://management.azure.com/subscriptions/${SUBSCRIPTION}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Web/sites/${WEBAPP_NAME}/slots/${SLOT_NAME}/extensions/onedeploy?api-version=2020-12-01"
Con esta URL y la de tu paquete la llamada a realizar será la siguiente:
# Deploy the web app using Kudu
az rest --method PUT \
--uri $SITE_URI \
--body '{
"properties": {
"packageUri": "'"${ZIP_URL}"'",
"type": "zip",
"ignorestack": false,
"clean": true,
"restart": false
}
}'
Una vez que esta se ejecute devolverá un resultado parecido a este:

Para comprobar su progreso puedes lanzar un GET sobre la misma URL, la cual te devolverá un array con todos los despliegues realizados hasta el momento y su estado:
az rest --method GET --uri $SITE_URI
Aunque siempre puedes coger el primero de los resultados, que será el último ejecutado, para saber cómo fue:
az rest --method GET --uri $SITE_URI | jq '.value[0].properties.provisioningState'
¡Saludos!