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
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
In case a scale-out event happens (!= scale-up), additional virtual machines are added to the ASP.
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.
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
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
$ 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.[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", "220.127.116.11", "18.104.22.168", "22.214.171.124,126.96.36.199", "188.8.131.52,184.108.40.206,220.127.116.11,18.104.22.168,22.214.171.124,126.96.36.199,188.8.131.52" ]
Please note, there are simpler methods to get to the IPs, I made this low-level call to provide details about the underlying
# 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.
- Configure regional virtual network integration from within your app service.
- Force all outbound traffic originating from that app to travel through the virtual network. This is done by setting
WEBSITE_VNET_ROUTE_ALL=1property in your web app configuration
- Create a public IP address.
- 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.
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
- Add a custom domain and verify your ownership.
- Create a self-signed certificate or order one
- Add a binding and upload your certificate
- 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
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.
- Create an Application Gateway inside a subnet
- Create a public static standard IP address
- Add your App Service as backend
- 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
- (Optionally) Lock down access to your App Service using IP Filtering
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! 😎