Notes for #Azure #ARM: how to perform conditional deployments


Notes for #Azure #ARM deployments: how to perform conditional deployments

I’ve already wrote about my ARM experiences and in the meantime I had time and chances to work more on the subject. While my first impressions have been confirmed, the model is becoming more and more convincing to me. As soon as you start to have a library of working snippets then deploying in any other way becomes a legacy of the past. Still there are pain points, the following is a short list from my previous article:

  • Complete lack of documentation. The documentation is “by examples”, but that’s not enough and many examples are simply broken
  • Lack of support across other Azure Services. For example, you cannot backup ARM based Virtual Machines with Azure Backup
  • If using ARM templates, I consider this a must, given the declarative model of ARM, you’re going to hit limitations and inconsistencies. For example, while you can instruct ARM to iterate part of you templates (i.e. I want to create 10 VMs like this) you cannot for others (i.e. Firewall or NAT rules when you want to apply some math to the port).
  • On the other hand the ARM model is much more convincing and potentially productive than the old one. After all, for Service Management we ended up defining our own declarative model and feeding the manifest with the characteristics of the artifacts to a powershell script. Much more there are activities that can only be performed in the ARM Portal (try to create a Premium Storage in the old portal if you can). Microsoft knows that this experience must be fixed, the sooner the better, it’s a matter of credibility. Now that customers are really moving to the public cloud, the providers must be up to the promises they made.

To these point I must add the whole API version story is a little cumbersome, especially when you-re forced to use different API versions for different resource types, I strongly believe every new API version should include all the previous released types plus any new additions needed.

But let’s go along and return to the subject of the article, or how to make conditioned choices inside and ARM template. With conditioned choices I’m basically referring to the need of using an IF inside your template, or a way to make choices based on ARM parameters, a few examples:

  • I want to create a vnet only if it’s not already present
  • If I pass an ssh key for my linux deployment then I want to use that kind of authentication, but if I pass a password I want to stick with username and password

Basically the opportunity of having an IF like approach in your ARM templates makes them more generic and reusable. Having created a marketplace solution for Azure I had the opportunity to experiment different techniques to achieve this goal, I must say the hash table is the way I prefer more, while it’s not the more concise way to do this, it’s by far the better readable way.

The building block to perform a conditional choice in ARM is the deployments type:

{
    "name": "my name",
    "type": "Microsoft.Resources/deployments",
    "apiVersion": "[variables('deploymentsApiVersion')]",
    "properties": {
    "mode": "Incremental",
    "templateLink": {
        "uri": "[variables('sharedTemplateUrl')]",
        "contentVersion": "1.0.0.0"
    },
    "parameters": {
        "param1": {
        "value": "[variables('param1')]"
        },
        "param2": {
        "value": "[parameters('param2')]"
        }
    }
    }
}

Basically the type Microsoft.Resources/depoloyments allows you to call an ARM template from another template. The key properties are the templateLink uri that sets the template to be included (or called, if you prefer) and the “parameters” signature (or the names of the parameters to be passed to the called template). The logic is pretty simple, if you’re able to dynamically change the template uri based on your own criteria, you’re basically implementing a choice that can be different at every run.
Let’s take a scenario where I want my template to use an existing vnet or to create a new one based on input parameters. First of all, our template must have an appropriate set of parameters, actually since I always invoke my templates using a powershell script, I started to use complex objects as input parameters instead of a long list of plain values. So in our case let’s assume our template has a parameter called networkSettings of type object with the following schema:

{
    "name": "myVnet",
    "resourceGroup": "vnetRG",
    "addressPrefix": "10.0.0.0/16",
    "newOrExisting": "new",
    "subnets": [{
    "mySubnet": {
        "name": "mySubnet",
        "addressPrefix": "10.0.0.0/24",
        "startAddress": "10.0.0.4"
    }],
}

The key property is newOrExisting, based on its value we can decide to use an existing subnet versus creating a new one. To achieve this, we need two templates with the same signature (same set of parameters), one to simply get subnet and/or vNet refernces of the existing vNet, the other one to create a brand new vNet and the required subnets. Then in the variables section of the main template we can define an hashtable such as:

    "vnetHash": {
      "new": "[concat(parameters('templateBaseUrl'),'partials/vnet-new.json')]",
      "existing": "[concat(parameters('templateBaseUrl'),'partials/vnet-existing.json')]"
    }

Basically we created a dictionary that has all the possible values for the property newOrExisting as keys and the related template names as values. The templateBaseUrl parameter is used to keep track of our template uri. If you don’t have any secret in it (and you shouldn’t) it can be an anonymously accessible uri, otherwise you can use an additional parameter with a SAS key to append to your uri. But that’s another story. Now that we have the hash we just need to access its members and get the proper template uri, this is easy in json using the [] construct>

    "vnetTemplate": "[variables('vnetHash')[parameters('networkSettings').newOrExisting]]"

and basically that’s all, in our main template resource section we just use the vnetTemplate variable to set our deployments resource, like this:

{
      "name": "[concat('vNet', deployment().name)]",
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "[variables('deploymentsApiVersion')]",
      "dependsOn": [
      ],
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[variables('vnetTemplate')]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "networkSettings": {
            "value": "[parameters('networkSettings')]"
          },
          "subnetName": { "value": "mySubnet" },
          "apiVersion": { "value": "[parameters('apiVersion')]" },
          "location": { "value": "[parameters('location')]" }
        }

      }
    }

This technique can be used every time you need to take conditionals actions in your templates, it can be expanded to more than one level of indirection to achieve more complex workflows. This is not the only technique and as I anticipated it’s not the more concise one, but I favor readability over conciseness in this kind of deployments (after all I’m not coding C, at least not anymore)
-Daniele

This posting is provided “AS IS” with no warranties, and confers no rights

Advertisements
  1. #1 by Dale on July 8, 2016 - 3:18 am

    Hi Daniele, did you manage to post complete templates? Would be ideal to see the whole thing in its entirety, including the sub-template for getting an existing resource vs. creating a new resource.

  2. #3 by 4c74356b41 on March 15, 2016 - 1:50 pm

    Wow, this is actually pretty cool! thanks!

  3. #4 by Jason on March 12, 2016 - 7:52 pm

    Excellent post. Would you mind sharing the complete JSON file(s)? The entry point json, and then the template files you link to? Your article is excellent, but it just helps me tremendously to be able to see the full file. Thank you for your work on this.

  1. Notes for #Azure #ARM: how to perform conditional deployments | asksven

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: