ARM Templates, conditional deployments in Azure
Tags: azure, devops, infraascode
Published: 2020-03-22 10:36:48

When you write a template, you want to use it to deploy several environments, the DEV, the QUAL, the Production. You also want to be able to re-run this template, for example, to add more resources. But between these different states, there is, sometimes, a minor difference, as the number of nodes you want to deploy, the VM size or other. Some resources can have different options depending on their SKU. How to deal with that?

There are two possibilities logical functions and deployment conditions. 

## Logical Functions

Logical functions, like other built-in functions in ARM templates, transform a value before using it in a resource or a variable.
There are few logical functions in ARM and the most used is the IF function. This function work like a IF {} ELSE {} statements in any programming language, except both IF and Else statement must only return a value.

[if(equals(parameters('variableToTest'), 'valueToTest'), 'valueIfTrue','valueIfFalse')]"

As you see if need a Boolean value. These values may come from a comparison function, an user defined function, a parameter or any functions returning true or false.

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "myParam": {
            "type": "string",
                "allowedValues": [
                    "Basic",
                    "Standard"
                ],
                "metadata": {
                    "description": "Test parameter"
                }
        },
        "myBoolparam": {
            "type": "bool"
        }
    },
    "variables": {},
    "resources": [],
    "outputs": {
        "outputMyParam": {
            "value": "[if(equals(parameters('myParam'), 'Standard'), 'Standard Value Selected','Basic Value Selected')]",
            "type" : "String"
        },
        "outputBoolean": {
             "value": "[if(parameters('myBoolparam'), 'True Selected','False Selected')]",
            "type" : "String"
        }
    }
}
```

```powershell
New-AzResourceGroupDeployment -Name test01 -ResourceGroupName myRG -TemplateFile .\Iffunction.json -myParam Basic -myBoolparam $true
```

```
Outputs                 :
                          Name             Type                       Value
                          ===============  =========================  ==========
                          outputMyParam    String                     Basic Value Selected
                          outputBoolean    String                     True Selected
```

You can combine multiple if with and AND 

```json
"outputAnd": {
            "value": "[and(equals(parameters('myParam'), 'Standard'), parameters('myBoolparam'))]",
            "type": "bool"
        }
```
It will return true if the parameter "myParam" equals Standard and the parameter “myBoolparam” is true. 
You can also use OR.

```json
"outputOr": {
            "value": "[or(equals(parameters('myParam'), 'Standard'), parameters('myBoolparam'))]",
            "type": "bool"
        }
```
It will return true if the parameter "myParam" equal Standard or the parameter "myBoolparam" is true.

The best practice is to use logical functions in the variable section.

```json
"variables": {
        "myVariable" : "[if(and(equals(parameters('myParam'), 'Standard'), parameters('myBoolparam')), 'Ok','notOk')]"
    },
```
And use it in your resources. 

For a more concrete example, if you want to deploy a Generation 2 VPN Gateway you need to use at least the VpnGw2 SKU. But for a non-production environment, you may want to use a less costly option. 

```json
       "gatewaySku": {
         "type": "string",
         "defaultValue": "VpnGw2",
         "allowedValues": [
           "Basic",
           "VpnGw1",
           "VpnGw2",
           "VpnGw3"
         ],
         "metadata": {
           "description": "The Sku of the Gateway. This must be one of Basic, Standard or HighPerformance."
         }
       }

     "variables": {
         "gatewayGeneration": "[if(equals(parameters('gatewaySku'), 'VpnGw1'), 'Generation1','Generation2')]"
 
     },
...
"properties": {
           "sku": {
             "name": "[parameters('gatewaySku')]",
             "tier": "[parameters('gatewaySku')]"
           },
           "gatewayType": "Vpn",
           "vpnType": "[parameters('vpnType')]",
           "enableBgp": false,
           "vpnGatewayGeneration": "[variables('gatewayGeneration')]"
         }
```
## Conditional deployment 

Sometimes it’s not just a value you want to change but an entire resource. Like in the example, a non-production environment may not need an availability Set or a load balancer. How can you deal with that? 
Instead of using two templates, one for the production and another for non-production, you can use the condition property in a resource. It will determine whether the resource is deployed. The value is a Boolean, true or false. 

"condition": "[equals(parameters('environment'), 'prod')]"

There are two things to know about condition property. The first one is how to deal with dependsOn properties. When you deploy resources that depend one or more are conditional resources on how to write the dependsOn clause? Should you include or not conditional resources in dependsOn?
You don’t have to care about that, The API will remove the undeployed resource from the dependsOn list automatically. 

```json
... 
    {
        "name": "myAvSet",
        "condition": "[equals(parameters('environement'), 'prod')]",
        "type": "Microsoft.Compute/availabilitySets",
        "apiVersion": "2019-07-01",
        "location": "[resourceGroup().location]",
        "tags": {
            "displayName": "Prod Av Set"
        },
        "properties": {
            "platformFaultDomainCount": 2,
            "platformUpdateDomainCount": 3
        },
        "sku": {
            "name": "Aligned"
        }
    }
...
    {
        "apiVersion": "2018-06-01",
        "type": "Microsoft.Compute/virtualMachines",
        "name": "vm01", 
        "location": "[resourceGroup().location]",
        "tags": "Vm Prod or Non Prod",
        "dependsOn": [
          "[concat('Microsoft.Network/networkInterfaces/', 'Vm01-01R-nic'))]",
          "[concat('Microsoft.Compute/availabilitySets/','myAvSet')]"
        ]
...
    }
```

If the environment parameter is not prod, Azure will remove the dependency from the virtual machine resource. 
The second thing to know is the deployment mode. There is no change when using Incremental mode but nn complete mode, when a conditional resource is re-deployed and the condition is false, if the resource API version is earlier than 2019-05-10 it will do nothing, but with the later version, the resource will be removed.

Conditional deployment can help you to manage how you can deploy multiple environments using only one template. It can be complicated, but for easier than having a bunch template files doing the same things.