This post describes a sample Azure template to create a user-defined route (UDR) in an Azure virtual network (Vnet) and associate that newly created route with a subnet. This sample JSON Azure Resource Manager (ARM) template is part of a series. The first post described how to create Azure Vnets in an ARM template. The second described how to peer two Azure virtual networks. The last post in the series is a sample template to create Azure Network Security Groups (NSG).
I also posted a way to create Vnets in PowerShell as a way to contrast creating Azure networking resources in JSON vs. PowerShell. What makes these samples different from others you might find are that they are designed to work across multiple Azure subscriptions. Also unlike most sample templates you will find, this ARM template example allows you to associate the newly created UDRs with subnets in a single deployment.
As I discovered during the development of these Azure templates, you are limited to accessing five Azure subscriptions in a single deployment. However, within those limits, you can create as many UDRs and subnet associations as you wish. And when you are finished, your parameter input becomes the as-built documentation of your Azure virtual network.
UDRs are the workhorses of routing in Azure Vnets. They allow you to do things like route all traffic to a gateway of your choice (like a CheckPoint or Palo Alto firewall appliance) or to a resource that’s acting as an internal load balancer. In short, if you are doing any kind of enterprise networking in Azure, you are going to need to configure UDRs and associate them with the subnets whose traffic routing should be modified. That’s especially the case in more security-conscious environments where you might need to isolate subnets from each other or you are using UDRs to route traffic to a central traffic monitoring appliance.
As with my other sample ARM templates in this series, this example uses the following techniques:
- It is a nested template, which means that the “outer” type of
Microsoft.Resources/deployment
will enable you to access multiple subscriptions (up to five) in the template. - It uses the
copy
function to create a runtime deployment for each subnet described in the parameter file (here, namedParameterArray
). - The names for resources are very important in this template. Note that the way this template works is that a
copy
function counts the number of outer (subnet) items in the parameter array and then in the nested template, acopyindex
function creates the UDR for each route defined in the inner array. These names are then used inMicrosoft.Network/virtualNetworks/subnets
to associate the route to the appropriate subnet. - In this sample, a series of database subnets have all their traffic (
0.0.0.0
) routed to a subnet in a different Vnet. This demonstrates how one might configure a series of DBMSs behind an internal firewall. - In the parameter file, there is an outer array of subnets and an inner array (for each subnet) of the routes for that subnet. Properties in the
routes
are from Microsoft.Network/routeTables/routes. Properties for the other resources are self-evident. But all names, for example, for resource group, Vnet and subscription must match exactly and/or be present before you run this template. - When I deploy this template, I use a “dummy” subscription and a “dummy” resource group to hold the deployment while resources are deployed to other subscriptions. That limits you to four of those other subscriptions so keep your parameter file input limited to less than four total if you deploy into different subscriptions than in the parameters or five if the deployment subscription will contain some of the routes you wish to create.
As always, good luck and I hope this works for you.
Here’s the template file, followed by the parameter file. Both are (c) 2018 Air11 Technology LLC and are licensed under the Apache OpenSource 2.0 license,
https://opensource.org/licenses/Apache-2.0
?{? "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",? "contentVersion": "1.0.0.0",? "parameters": {? "ParameterArray": {? "type": "array"? }? },? "variables": {},? "resources": [? {? "apiVersion": "2017-05-10",? "type": "Microsoft.Resources/deployments",? "copy": {? "name": "Udrs",? "count": "[length(parameters('ParameterArray'))]"? },? "name": "[concat('Deploy-Udr-in-',parameters('ParameterArray')[copyIndex('Udrs')].Subnet)]",? "subscriptionId": "[parameters('ParameterArray')[copyIndex('Udrs')].SubscriptionId]",? "resourceGroup": "[parameters('ParameterArray')[copyIndex('Udrs')].ResourceGroupName]",? "properties": {? "mode": "Incremental",? "template": {? "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json",? "contentVersion": "1.0.0.0",? "variables": {},? "resources": [? {? "apiVersion": "2017-10-01",? "type": "Microsoft.Network/routeTables",? "name": "[concat('Udr-',parameters('ParameterArray')[copyIndex('Udrs')].Subnet)]",? "location": "[parameters('ParameterArray')[copyIndex('Udrs')].Region]",? "properties": {? "copy": [? {? "name": "routes",? "count": "[length(parameters('ParameterArray')[copyIndex('Udrs')].routes)]",? "input": {? "name": "[parameters('ParameterArray')[copyIndex('Udrs')].routes[copyIndex('routes')].name]",? "properties": {? "addressPrefix": "[parameters('ParameterArray')[copyIndex('Udrs')].routes[copyIndex('routes')].addressPrefix]",? "nextHopType": "[parameters('ParameterArray')[copyIndex('Udrs')].routes[copyIndex('routes')].nextHopType]",? "nextHopIpAddress": "[parameters('ParameterArray')[copyIndex('Udrs')].routes[copyIndex('routes')].nextHopIpAddress]"? }? }? }? ]? }? },? {? "apiVersion": "2018-03-01",? "type": "Microsoft.Network/virtualNetworks/subnets",? "name": "[concat(parameters('ParameterArray')[copyIndex('Udrs')].VnetName, '/', parameters('ParameterArray')[copyIndex('Udrs')].subnet)]",? "dependsOn": [? "[concat('Udr-',parameters('ParameterArray')[copyIndex('Udrs')].Subnet)]"? ],? "location": "[parameters('ParameterArray')[copyIndex('Udrs')].Region]",? "properties": {? "addressPrefix": "[parameters('ParameterArray')[copyIndex('Udrs')].SubnetAddressPrefix]",? "routeTable": {? "id": "[resourceId(parameters('ParameterArray')[copyIndex('Udrs')].SubscriptionId,parameters('ParameterArray')[copyIndex('Udrs')].ResourceGroupName,'Microsoft.Network/routeTables',concat('Udr-',parameters('ParameterArray')[copyIndex('Udrs')].Subnet))]"? }? }? }? ]? }? }? }? ],? "outputs": {}?}??{? "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",? "contentVersion": "0.0.0.1",? "parameters": {? "ParameterArray": {? "value": [? {? "Subnet": "Subnet-10-120-24-0-26-SQL-Server-1",? "SubnetAddressPrefix": "10.120.24.0/26",? "VnetName": "VnetSharedDatabasesDev",? "ResourceGroupName": "RgSharedDatabasesDev",? "SubscriptionId": "12345678-8888-0000-1234-123456789ABC",? "Region": "eastus2",? "routes": [? {? "name": "Udr-On-Subnet-10-120-24-0-26-SQL-Server-1-To-InternalAccess",? "nextHopType": "VirtualAppliance",? "nextHopIpAddress": "10.120.8.10",? "addressPrefix": "0.0.0.0/0"? }? ]? },? {? "Subnet": "Subnet-10-120-24-64-26-SQL-Server-2",? "SubnetAddressPrefix": "10.120.24.64/26",? "VnetName": "VnetSharedDatabasesDev",? "ResourceGroupName": "RgSharedDatabasesDev",? "SubscriptionId": "12345678-8888-0000-1234-123456789ABC",? "Region": "eastus2",? "routes": [? {? "name": "Udr-On-Subnet-10-120-24-64-26-SQL-Server-1-To-InternalAccess",? "nextHopType": "VirtualAppliance",? "nextHopIpAddress": "10.120.8.10",? "addressPrefix": "0.0.0.0/0"? }? ]? },? {? "Subnet": "Subnet-10-120-24-128-26-Oracle-1",? "SubnetAddressPrefix": "10.120.24.128/26",? "VnetName": "VnetSharedDatabasesDev",? "ResourceGroupName": "RgSharedDatabasesDev",? "SubscriptionId": "12345678-8888-0000-1234-123456789ABC",? "Region": "eastus2",? "routes": [? {? "name": "Udr-On-Subnet-10-120-24-128-26-Oracle-1",? "nextHopType": "VirtualAppliance",? "nextHopIpAddress": "10.120.8.10",? "addressPrefix": "0.0.0.0/0"? }? ]? },? {? "Subnet": "Subnet-10-120-24-192-26-Oracle-2",? "SubnetAddressPrefix": "10.120.24.192/26",? "VnetName": "VnetSharedDatabasesDev",? "ResourceGroupName": "RgSharedDatabasesDev",? "SubscriptionId": "12345678-8888-0000-1234-123456789ABC",? "Region": "eastus2",? "routes": [? {? "name": "Udr-On-Subnet-10-120-24-192-26-Oracle-2",? "nextHopType": "VirtualAppliance",? "nextHopIpAddress": "10.120.8.10",? "addressPrefix": "0.0.0.0/0"? }? ]? }?? ]? }? }?}
Leave a Reply