Tutorial: Isolate back-end communication in Azure App Service with Virtual Network integration

In this article you will configure an App Service app with secure, network-isolated communication to backend services. The example scenario used is in Tutorial: Secure Cognitive Service connection from App Service using Key Vault. When you're finished, you have an App Service app that accesses both Key Vault and Cognitive Services through an Azure virtual network (VNet), and no other traffic is allowed to access those back-end resources. All traffic will be isolated within your VNet using VNet integration and private endpoints.

As a multi-tenanted service, outbound network traffic from your App Service app to other Azure services shares the same environment with other apps or even other subscriptions. While the traffic itself can be encrypted, certain scenarios may require an extra level of security by isolating back-end communication from other network traffic. These scenarios are typically accessible to large enterprises with a high level of expertise, but App Service puts it within reach with VNet integration.

scenario architecture

With this architecture:

  • Public traffic to the back-end services is blocked.
  • Outbound traffic from App Service is routed to the VNet and can reach the back-end services.
  • App Service is able to perform DNS resolution to the back-end services through the private DNS zones.

What you will learn:

  • Create a VNet and subnets for App Service VNet integration
  • Create private DNS zones
  • Create private endpoints
  • Configure VNet integration in App Service

Prerequisites

The tutorial assumes that you have followed the Tutorial: Secure Cognitive Service connection from App Service using Key Vault and created the language detector app.

The tutorial continues to use the following environment variables from the previous tutorial. Make sure you set them properly.

    groupName=myKVResourceGroup
    region=westeurope
    csResourceName=<cs-resource-name>
    appName=<app-name>
    vaultName=<vault-name>

Create VNet and subnets

  1. Create a VNet. Replace <virtual-network-name> with a unique name.

    # Save vnet name as variable for convenience
    vnetName=<virtual-network-name>
    
    az network vnet create --resource-group $groupName --location $region --name $vnetName --address-prefixes 10.0.0.0/16
    
  2. Create a subnet for the App Service VNet integration.

    az network vnet subnet create --resource-group $groupName --vnet-name $vnetName --name vnet-integration-subnet --address-prefixes 10.0.0.0/24 --delegations Microsoft.Web/serverfarms
    

    For App Service, the VNet integration subnet is recommended to have a CIDR block of /26 at a minimum (see VNet integration subnet requirements). /24 is more than sufficient. --delegations Microsoft.Web/serverfarms specifies that the subnet is delegated for App Service VNet integration.

  3. Create another subnet for the private endpoints.

    az network vnet subnet create --resource-group $groupName --vnet-name $vnetName --name private-endpoint-subnet --address-prefixes 10.0.1.0/24 --disable-private-endpoint-network-policies
    

    For private endpoint subnets, you must disable private endpoint network policies.

Create private DNS zones

Because your Key Vault and Cognitive Services resources will sit behind private endpoints, you need to define private DNS zones for them. These zones are used to host the DNS records for private endpoints and allow the clients to find the back-end services by name.

  1. Create two private DNS zones, one for your Cognitive Services resource and one for your key vault.

    az network private-dns zone create --resource-group $groupName --name privatelink.cognitiveservices.azure.com
    az network private-dns zone create --resource-group $groupName --name privatelink.vaultcore.azure.net
    

    For more information on these settings, see Azure Private Endpoint DNS configuration

  2. Link the private DNS zones to the VNet.

    az network private-dns link vnet create --resource-group $groupName --name cognitiveservices-zonelink --zone-name privatelink.cognitiveservices.azure.com --virtual-network $vnetName --registration-enabled False
    az network private-dns link vnet create --resource-group $groupName --name vaultcore-zonelink --zone-name privatelink.vaultcore.azure.net --virtual-network $vnetName --registration-enabled False
    

Create private endpoints

  1. In the private endpoint subnet of your VNet, create a private endpoint for your key vault.

    # Get Cognitive Services resource ID
    csResourceId=$(az cognitiveservices account show --resource-group $groupName --name $csResourceName --query id --output tsv)
    
    az network private-endpoint create --resource-group $groupName --name securecstext-pe --location $region --connection-name securecstext-pc --private-connection-resource-id $csResourceId --group-id account --vnet-name $vnetName --subnet private-endpoint-subnet
    
  2. Create a DNS zone group for the Cognitive Services private endpoint. DNS zone group is a link between the private DNS zone and the private endpoint. This link helps you to auto update the private DNS Zone when there is an update to the private endpoint.

    az network private-endpoint dns-zone-group create --resource-group $groupName --endpoint-name securecstext-pe --name securecstext-zg --private-dns-zone privatelink.cognitiveservices.azure.com --zone-name privatelink.cognitiveservices.azure.com
    
  3. Block public traffic to the Cognitive Services resource.

    az rest --uri $csResourceId?api-version=2021-04-30 --method PATCH --body '{"properties":{"publicNetworkAccess":"Disabled"}}' --headers 'Content-Type=application/json'
    
    # Repeat following command until output is "Succeeded"
    az cognitiveservices account show --resource-group $groupName --name $csResourceName --query properties.provisioningState
    

    Note

    Make sure the provisioning state of your change is "Succeeded". Then you can observe the behavior change in the sample app. You can still load the app, but if you try click the Detect button, you get an HTTP 500 error. The app has lost its connectivity to the Cognitive Services resource through the shared networking.

  4. Repeat the steps above for the key vault.

    # Create private endpoint for key vault
    vaultResourceId=$(az keyvault show --name $vaultName --query id --output tsv)
    az network private-endpoint create --resource-group $groupName --name securekeyvault-pe --location $region --connection-name securekeyvault-pc --private-connection-resource-id $vaultResourceId --group-id vault --vnet-name $vnetName --subnet private-endpoint-subnet
    # Create DNS zone group for the endpoint
    az network private-endpoint dns-zone-group create --resource-group $groupName --endpoint-name securekeyvault-pe --name securekeyvault-zg --private-dns-zone privatelink.vaultcore.azure.net --zone-name privatelink.vaultcore.azure.net
    # Block public traffic to key vault
    az keyvault update --name $vaultName --default-action Deny
    
  5. Force an immediate refetch of the key vault references in your app by resetting the app settings (for more information, see Rotation).

    az webapp config appsettings set --resource-group $groupName --name $appName --settings CS_ACCOUNT_NAME="@Microsoft.KeyVault(SecretUri=$csResourceKVUri)" CS_ACCOUNT_KEY="@Microsoft.KeyVault(SecretUri=$csKeyKVUri)"
    

    Note

    Again, you can observe the behavior change in the sample app. You can no longer load the app because it can no longer access the key vault references. The app has lost its connectivity to the key vault through the shared networking.

The two private endpoints are only accessible to clients inside the VNet you created. You can't even access the secrets in the key vault through Secrets page in the Azure portal, because the portal accesses them through the public internet (see Manage the locked down resources).

Configure VNet integration in your app

  1. Scale the app up to Standard tier. VNet integration requires Standard tier or above (see Integrate your app with an Azure virtual network).

    az appservice plan update --name $appName --resource-group $groupName --sku S1
    
  2. Unrelated to our scenario but also important, enforce HTTPS for inbound requests.

    az webapp update --resource-group $groupName --name $appName --https-only
    
  3. Enable VNet integration on your app.

    az webapp vnet-integration add --resource-group $groupName --name $appName --vnet $vnetName --subnet vnet-integration-subnet
    

    VNet integration allows outbound traffic to flow directly into the VNet. By default, only local IP traffic defined in RFC-1918 is routed to the VNet, which is what you need for the private endpoints. To route all your traffic to the VNet, see Manage virtual network integration routing. Routing all traffic can also be used if you want to route internet traffic through your VNet, such as through an Azure VNet NAT or an Azure Firewall.

  4. In the browser, navigate to <app-name>.azurewebsites.net again and wait for the integration to take effect. If you get an HTTP 500 error, wait a few minutes and try again. If you can load the page and get detection results, then you're connecting to the Cognitive Services endpoint with key vault references.

    Note

    If keep getting HTTP 500 errors after a long time, it may help to force a refetch of the key vault references again, like so:

    az webapp config appsettings set --resource-group $groupName --name $appName --settings CS_ACCOUNT_NAME="@Microsoft.KeyVault(SecretUri=$csResourceKVUri)" CS_ACCOUNT_KEY="@Microsoft.KeyVault(SecretUri=$csKeyKVUri)"
    

Manage the locked down resources

Depending on your scenarios, you may not be able to manage the private endpoint protected resources through the Azure portal, Azure CLI, or Azure PowerShell (for example, Key Vault). These tools all make REST API calls to access the resources through the public internet, and are blocked by your configuration. Here are a few options for accessing the locked down resources:

  • For Key Vault, add the public IP of your local machine to view or update the private endpoint protected secrets.
  • If your on premises network is extended into the Azure VNet through a VPN gateway or ExpressRoute, you can manage the private endpoint protected resources directly from your on premises network.
  • Manage the private endpoint protected resources from a jump server in the VNet.
  • Deploy Cloud Shell into the VNet.

Clean up resources

In the preceding steps, you created Azure resources in a resource group. If you don't expect to need these resources in the future, delete the resource group by running the following command in the Cloud Shell:

az group delete --name $groupName

This command may take a minute to run.

Next steps