Efficient code in ARM

Ronald Buys
Dec 31, 2020 · 4 min read

How do we save lines of code within ARM, especially when we have something like an Application Gateway? Keep on reading if you already have basic knowledge of ARM, if not I suggest you read this first: Templates overview — Azure Resource Manager | Microsoft Docs

So what is an Application Gateway (in short)?

Azure Application Gateway is a web traffic load balancer that enables you to manage traffic to your web applications. Traditional load balancers operate at the transport layer (OSI layer 4 — TCP and UDP) and route traffic based on source IP address and port, to a destination IP address and port.

But that’s not what we are gonna talk about today! We are gonna talk about ARM and building this resource into an ARM-template.

Challenges with Application Gateway in ARM…

So perhaps you already tried to build an ARM-template for your Application Gateway and ended up with many… many… many… okay you get it, many lines of codes. Resulting in an ARM-template hard to read and maintain.

Well we faced the same challenge as you did. Simply because a resource like the Application Gateway has many options and settings to configure.

How did we solved this?

It started with the request to deploy an Application Gateway with 50 listeners, 20 SSL-certificates, multiple- backends, probes and routing rules.

So our first thought: ‘This is gonna end up having an ARM-template with too many lines of code. We will go crazy on this… I am sure… what to do!? Also maintaining this will be impossible.’

Well first thing to do is check how the ARM-engine can handle multiple entries in a template. So first we learned about the copy-function, but we cannot use nested copies? :( Great! A limitation of the ARM-engine…

You can find each function for ARM within the link provided below:

The copy-function however (yes) is the way to go! But how to integrate this into our ARM-template? Let’s look at an example below where we are configuring the frontendIPConfiguration:

"copy": [
{
"name": "frontendIPConfigurations",
"count": "[length(parameters('frontendIPConfigurations'))]",
"input": {
"name": "[parameters('frontendIPConfigurations') [copyIndex('frontendIPConfigurations')].Name]",
"properties": "[parameters('frontendIPConfigurations')[copyIndex('frontendIPConfigurations')].properties]"
}
}
]

So in this example we see our copy-function with the section frontendIPConfiguration. Let’s drill it down:

  1. Name: required because this is a property of the properties within the Application Gateway.
  2. Count: let’s count how many items we have in the parameter frontendIPConfiguration.
  3. Input: here we define the input of each item in the parameter frontendIPConfiguration.
  4. Name: this is the name of the frontendIPConfiguration.
  5. Properties: well we just paste the property of a frontendIPConfiguration item.

This will give you the following result:

"frontendIPConfigurations": [
{
"name": "CONTOSO-WE-C-WEB-ACC-AGW01-PIP-PUBLIC",
"properties": {
"subnet": {
"id": "..."
},
"publicIPAddress": {
"id": "..."
}
}
}
]

So in the copy-section we are using a parameter which was set at the top of our deploy-file with the type object. But how does this line of code ends up within our parameter-file? Well lets take a look at the parameter-file because this is where the dynamic part of this template comes together:

"frontendIPConfigurations": {
"value": [
{
"name": "[variables('frontendPublicIPName')]",
"properties": {
"PublicIPAddress": {
"id": "[variables('appGwPublicIpRef')]"
}
}
},
{
"name": "[variables('frontendPrivateIPName')]",
"properties": {
"privateIpAddress": "[parameters('privateIP')]",
"privateIpAllocationMethod": "Static",
"subnet": {
"id": "[variables('subnetRef')]"
}
}
}
]
}

So what we can see is that the object frontendIPConfiguration is configured within our parameter-file with the values name and properties. Both are used within the deploy-file, here is an example of the property Name:

"[parameters('frontendIPConfigurations')[copyIndex('frontendIPConfigurations')].Name]"

Now we have created an object containing one or more setting(s) for our Application Gateway.

So in the case we want to do the same for HTTPListeners:

And here is our parameter-file:

"HTTPListeners": {
"value": [
{
"hostname": "private.MCA.nl",
"port": "443",
"protocol": "HTTPS",
"SniEnabled": true,
"configureprivateIp": true
},
{
"hostname": "private.MCA.nl",
"port": "80",
"protocol": "HTTP",
"SniEnabled": false,
"configureprivateIp": true
}
]
}

Again we can see that the parameter of type object contains more then a single item. Here we are creating HTTPListeners for private.mca.nl for both HTTP and HTTPS. There is a extra property present named configurePrivateIP which we added to create more functionality to the ARM-template. This property allows you to configure both Public- as Private IP addresses.

So to wrap it up we just showed you:

  • how to use parameters of type object and let ARM create more then a single configuration property on the Application Gateway.
  • how to use your own properties like the configurePrivateIP property and make use of IF-statements to configure both Public- and Private IP Addresses.

We did this on the frontendIPConfiguration and HTTPListeners. But this also applies to the other parts of the Application Gateway, like:

  • HTTPListeners
  • backendAddressPools
  • backendHttpSettingsCollection
  • probes
  • requestRoutingRules
  • redirectConfigurations
  • rewriteRuleSets

Do you want to know more? Please contact me or Wortell.

Wortell

Microsoft Cloud & Enterprise Security