Avoiding an easy mistake when deploying a Regional VNet Integration with ARM
Deploying a Regional VNet Integration with ARM is easy to get wrong, especially if you only have the official documentation to work from. In this post I'll explain an easy mistake you can make the first time you use an ARM template to set up a Regional VNet Integration, show you the correct resource type to use, and show how you can diagnose the problem if you get it wrong.
Azure App Service has a networking feature called VNet Integration, which allows outbound traffic from your App Service app to be pushed into a private VNet rather than go directly to the public internet. This is useful when you need to reach resources or servers inside a peered VNet, or over an ExpressRoute connection to on-premises, or send all outbound traffic via an egress controller on your security perimeter, or ... etc etc.
The documentation is really quite good at explaining the feature, and when, where, why, and how you use it.
There are two distinct flavours of VNet Integration; Regional and Gateway-Required. Gateway-Required has been around for a while and uses a point-to-site VPN to get traffic into your VNet. Regional is much newer and simpler to use.
Also Known As - AKA:
There have been a variety of terms attached to the Regional VNet Integration feature throughout its development and release, and you sometimes see them in documentation and APIs - you might see some of them in your travels. They all refer in some way to Regional VNet Integration as opposed to Gateway-Required:
- Swift - this one comes up in the REST API methods and payloads, and in ARM templates.
- New VNet - common in blog posts and articles from 2018 to late 2019, when Regional was still in preview or only just GA (Generally Available).
- VNet Injection - the docs sometimes refer to VNet injection without defining the term.
How NOT to deploy Regional VNet Integration
If, like me, you go to the ARM reference documentation and look under Microsoft.Web, you'll see sites/virtualNetworkConnections
and sites/slots/virtualNetworkConnections
.
🛑 Warning! This is not the ARM resource type you want! This is for Gateway-Required VNet Integration. 🛑
If you use a Microsoft.Web/sites/virtualNetworkConnections resource type, you'll configure a Gateway-Required integration. Even if you don't configure the Gateway-Required integration correctly, which I didn't, ARM won't report any issues and the integration will not work.
A helpful Microsoft support engineer was able to diagnose my problem - which was that I had incorrectly configured a Gateway-Required VNet Integration - and pointed me to the correct way to set up my intended Regional VNet integration with ARM.
The solution
Stackoverflow to the rescue!
See the solution in context on Stack Overflow.
The resource type you want is Microsoft.Web/sites/networkConfig
(and .../sites/slots/...
if you're using them), not Microsoft.Web/sites/virtualNetworkConnections
.
{
"type": "Microsoft.Web/sites/networkConfig",
"name": "[concat(parameters('webAppName'),'/VirtualNetwork')]",
"apiVersion": "2016-08-01",
"properties":
{
"subnetResourceId": "[parameters('subnetResourceId')]",
"swiftSupported": true
}
}
Microsoft.Web/sites/networkConfig
appears to be entirely undocumented in the official ARM docs, but it does appear in the ARM schema, which you can find on GitHub. Note the end of the name
must be /virtualNetwork
, you can't change it.
That's it for the solution. If you're interested in knowing how to tell whether the thing you've configured is a Gateway-Required or Regional VNet Integration, read on!
The diagnosis
Our friend here is the Azure Resource Explorer. If you've never seen it, take a moment to explore, it's a really useful way to see details that are not surfaced in the Azure portal.
You can follow these steps to check whether you've set up your Regional VNet Integration correctly.
Navigate to the web app's config/virtualNetwork
node
Subscriptions --> {subscription} --> resourceGroups --> {resource group} --> providers --> Microsoft.Web --> {web app} --> config --> virtualNetwork
Note the properties you set in the ARM template. The properties you see in the Resource Explorer are not exactly the same as what you set in the ARM template, but they're pretty close:
"name": "virtualNetwork"
"type": "Microsoft.Web/sites/config"
"swiftEnabled": true
Navigate to the web app's site/virtualNetworkConnections
node
Subscriptions --> {subscription} --> resourceGroups --> {resource group} --> providers --> Microsoft.Web --> {web app} --> virtualNetworkConnections
Note the value of the name
property, this is the important bit;
"name": "{guid}_{app-service-plan-name}"
Regional VNet Integration will result in the name field derived from the resourceGuid
of the VNet and the name of the app service plan, {guid}_{app-service-plan-name}
. If, like me, you used ARM to create a virtualNetworkConnections
sub-resource (and hence created a Gateway-Required integration by mistake), then the name will not have the {guid}_{app-service-plan-name}
format but will be whatever you set as the resource name in your ARM template.
There's also the isSwift
property. If it's set to true, then it means you've set up a Regional VNet Integration like you wanted.
Summary
- Gateway-Required VNet Integration is configured with the ARM resource
Microsoft.Web/sites/virtualNetworkConnections
. You can't use this for Regional. - Regional VNet Integration is configured with
Microsoft.Web/sites/networkConfig
(orMicrosoft.Web/sites/slots/networkConfig
) - No it's not documented in the ARM reference docs 😑
- StackOverflow has the answer
- There are clues in the Azure Resource Explorer if you look carefully
Resources
- App Service VNet Integration
- ARM resource type Microsoft.Web/sites/virtualNetworkConnections. This is the resource type you use for setting up a Gateway-Required integration, not a Regional integration.
- Stackoverflow answer with the solution
- Azure Resource Explorer
- ARM schema showing the Microsoft.Web/sites/networkConfig resource type and the properties type you use with it.
You can find the original version of this post on Anthony's personal blog.