How to use static IP addresses with Azure App Service

How to use static IP addresses with Azure App Service

Introduction

There might be situations where you have to ensure that your App Service is using a fixed IP address.

For example when your web application has to call an external endpoint that has restrictive inbound firewall rules which you can't control. Another situation might be that the lifecycle of your app service is bound to a CI/CD pipeline. And you don't want the app service to have a new IP address every time it gets recreated.

What ever your reasons might be, in this post I am going to show you how to use static IP addresses together with Azure App Service. But first, let's see why the change generally.

When do IP addresses change?

Microsoft lists the following events that will result in a change of the IP addresses:

Inbound IP Outbound IP
You delete an app & recreate it in a different resource group You delete an app & recreate it in a different resource group
You delete the last app in a resource group & region combination and recreate it You delete the last app in a resource group & region combination and recreate it
You delete an existing IP-based TLS/SSL binding, such as during certificate renewal You scale your app between the lower tiers (Basic, Standard, Premium) and the Premium V2 tier

To understand why the events from above cause an IP change, we need to understand what webspaces are.

What are webspaces?

In general, a webspace can be seen as a virtual grouping of application service plans (ASP). Whereas an ASP is basically a group of virtual machines running one ore more websites. That's the reason why ASPs internally are called serverFarms. App Services on the other hand are internally called sites.

In case a scale-out event happens (!= scale-up), additional virtual machines are added to the ASP.

Now this webspaces, or deployment units, can not be created by the user. They are managed for us by the Azure Resource Manager and are covered under the hood. The following diagram provides an overview.

Azure Webspaces (deployment units)

Now what's important to note here, is that all plans created with the same resource-group / region combination end up in the same webspace. And further, all public IP addresses are bound to this webspace. Let's dive a little deeper.

Here I am making a low-level call to Azure Resource Manager, requesting details about my ASPs which are inside the resource group rg-app-services.

As you can see from the output below, I got two ASPs, one in region North Europe and one in region West Europe. Both belong to different webspaces, which are called rg-app-services-NorthEuropewebspace and rg-app-services-WestEuropewebspace.

$ az rest \
--method get \
--url https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/rg-app-services/providers/Microsoft.Web/serverfarms?api-version=2018-02-01 \
--query value[].[name,type,location,properties.serverFarmId,properties.webSpace]

[
  [
    "asp-linux-free",
    "Microsoft.Web/serverfarms",
    "North Europe",
    1650,
    "rg-app-services-NorthEuropewebspace"
  ],
  [
    "asp-windows-free",
    "Microsoft.Web/serverfarms",
    "West Europe",
    13109,
    "rg-app-services-WestEuropewebspace"
  ]
]

This fact explains the table provided from above. If you delete and recreate an app service in a different resource group, it ends up in a different webspace having different public IP addresses attached to it!

Below I am querying Azure Resource Manager again, but this time for site details. It also carries a property called properties.webSpace, and some more details about possible inbound and outbound IP addresses.

$ az rest \
--method get \
--url https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/rg-app-services/providers/Microsoft.Web/sites?api-version=2018-02-01 \
--query value[0].[name,properties.webSpace,type,location,properties.inboundIpAddress,properties.possibleInboundIpAddresses,properties.outboundIpAddresses,properties.possibleOu
tboundIpAddresses]

[
  "azureblue-demo1",
  "rg-app-services-NorthEuropewebspace",
  "Microsoft.Web/sites",
  "North Europe",
  "13.69.228.0",
  "13.69.228.0",
  "13.69.228.0,13.79.17.70",
  "13.69.228.0,13.79.17.70,40.69.68.183,13.79.2.202,40.127.170.154,40.127.162.107,13.79.5.187"
]

Please note, there are simpler methods to get to the IPs, I made this low-level call to provide details about the underlying webspace, only.

# Find inbound IPs
nslookup <app-name>.azurewebsites.net

# Find outbound IPs
az webapp show --resource-group <group_name> --name <app_name> --query outboundIpAddresses --output tsv

# Find all possible outbound IPs
az webapp show --resource-group <group_name> --name <app_name> --query possibleOutboundIpAddresses --output tsv

Now enough with the details, let's see how we finally can used fixed IPs.

Using a static outbound IP

Use Azure NAT Gateway

One way is to leverage a NAT Gateway for this purpose and you will end up with an architecture as depicted in the diagram.

Azure App Service using a fixed outbound IP address

Steps

  1. Configure regional virtual network integration from within your app service.
  2. Force all outbound traffic originating from that app to travel through the virtual network. This is done by setting WEBSITE_VNET_ROUTE_ALL=1 property in your web app configuration
  3. Create a public IP address.
  4. Add a NAT gateway, attach it to the subnet that contains the app service and make use of the public IP created in step 3.  

Constraints

There are a couple of restrictions worth mentioning. First, this approach requires an App Service Plan that supports virtual network integration (Standard or Premium). And second, the inbound IP could still change.

Using a static inbound IP

Use TLS-Binding

Steps

  1. Add a custom domain and verify your ownership.
  2. Create a self-signed certificate or order one
  3. Add a binding and upload your certificate
  4. Upload and set to "SNI SSL"

Here is a PowerShell script to create a self signed certificate.

$cert = New-SelfSignedCertificate -DnsName demo.azureblue.io -CertStoreLocation "Cert:\CurrentUser\My"
$secret = ConvertTo-SecureString -String "Abracadabra" -Force -AsPlainText
Export-PfxCertificate -FilePath C:\Temp\demo-azureblue-io.pfx -Password $secret -Cert $cert
Get-ChildItem "Cert:\CurrentUser\My" | where Subject "CN=demo.azureblue.io" | Remove-Item

Constraints

To create custom security bindings, your App Service plan must be in the Basic, Standard or Premium tier.

Use Azure Application Gateway

I am not going to cover all the details required for this setup. But the high-level steps should get you going. The trick is to use the gateway together with service endpoints to reach our desired result.

Steps

  1. Create an Application Gateway inside a subnet
  2. Create a public static standard IP address
  3. Add your App Service as backend
  4. Enable service endpoints (Microsoft.Web) for the subnet holding your gateway, so that incoming traffic (from Internet via Gateway) to the App Service travels through the Azure Backbone
  5. (Optionally) Lock down access to your App Service using IP Filtering
Azure App Service using Application Gateway

Conclusion

As we have seen we can use a NAT Gateway to get a static outbound IP, and TLS-Binding or an Application Gateway for static inbound IPs.

Of course no-one stops you from mixing a NAT Gateway together with TLS-Binding, to fix both, incoming and outgoing IPs.

So that's it for today. I hope you found this post informative, and as always feedback is welcome! Stay well! 😎

Further links

Inbound/Outbound IP addresses - Azure App Service
Learn how inbound and outbound IP addresses are used in Azure App Service, when they change, and how to find the addresses for your app.
Secure a custom DNS with a TLS/SSL binding - Azure App Service
Secure HTTPS access to your custom domain by creating a TLS/SSL binding with a certificate. Improve your website’s security by enforcing HTTPS or TLS 1.2.
Application Gateway integration with service endpoints - Azure App Service
Describes how Application Gateway integrates with Azure App Service secured with service endpoints.
Show Comments