ARM your dev pipe with CALM

Nowadays starting a new project demands a lot knowledge. Besides your programming skills, you also need to know source control, continuous integration and delivery techniques as well as taming cloud solutions. As the time to market decreases and agile working increases your releases, you better start thinking about automating your CI/CD pipeline.

Don't repeat yourself

We're devs because we want to automate things. After creating some projects and releasing them to Azure, I noticed that I created ARM templates over and over again. Guilty of copy pasting resources from one ARM template to another I needed to face the dev inside of me and create a more structured way of creating ARM deploys.

Cloud Application Lifecycle Management (CALM)

I started with creating ARM templates that I could reuse. I came up with the following solution:
CALM overview.

  • config: this file contains all the links towards the other templates. So, from your master template you only need to reference this file.
  • naming convention: because each company can have its own naming conventions, the conventions are combined in this file.
  • other: all the other files are template files that can be used independently, but have a huge value when combined with the naming convention file.

Azure Naming conventions

The calm_namingconvention.json file contains an implementation of a naming convention. The default naming has currently the following structure:

<companyName>-<projectName><environmentAffix>-[<role>]<resource>  

If you don't like it, you just need to replace that file and all your resources will get the name you defined there.

T-shirt sizing

You can find many ARM tips on the internet. One of them is making use of t-shirt sizing. Instead of defining a B1 or S1 for an app service plan you just provide "small", "medium" or "large" and the template will translate the small into a meaningful way for the resource.

Master template

The master template contains the setup where the config file can be found. It also needs to tell that it depends on the naming conventions. A base template for your master can look like:

{
  "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "companyName": {
      "type": "string",
      "defaultValue": "foo",
      "minLength": 3,
      "maxLength": 10,
      "metadata": {
        "description": "The company name this set of parameters appliest to."
      }
    },
    "projectName": {
      "type": "string",
      "defaultValue": "bar",
      "minLength": 3,
      "maxLength": 10,
      "metadata": {
        "description": "The project name this set of parameters appliest to."
      }
    },
    "environmentName": {
      "type": "string",
      "defaultValue": "Development",
      "allowedValues": [
        "Development",
        "Acceptance",
        "Production"
      ],
      "metadata": {
        "description": " The environment this set of parameters applies to."
      }
    },
    "environmentSize": {
      "type": "string",
      "defaultValue": "small",
      "allowedValues": [
        "small",
        "medium",
        "large"
      ],
      "metadata": {
        "description": "T-shirt size for resources"
      }
    },
    "configurationUrl":{
      "type": "string",
      "defaultValue": "https://raw.githubusercontent.com/jtourlamain/DevProtocol.Calm/master/armtemplates/calm_config.json",
      "metadata": {
        "description": "The url where the calm configuration can be found. The configuration contains the urls to all othere templates"
      }
    }
  },
  "variables": {
  },
  "resources": [
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2015-01-01",
      "name": "configurationTemplate",
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[parameters('configurationUrl')]",
          "contentVersion": "1.0.0.0"
        }
      }
    },
    {
      "type": "Microsoft.Resources/deployments",
      "apiVersion": "2015-01-01",
      "name": "namingconventionTemplate",
      "properties": {
        "mode": "Incremental",
        "templateLink": {
          "uri": "[reference('configurationTemplate').outputs.namingconventionTemplateUrl.value]",
          "contentVersion": "1.0.0.0"
        },
        "parameters": {
          "companyName": {
            "value": "[parameters('companyName')]"
          },
          "projectName": {
            "value": "[parameters('projectName')]"
          },
          "environmentName": {
            "value": "[parameters('environmentName')]"
          }
        }
      },
      "dependsOn": [
        "configurationTemplate"
      ]
    }

// Place the resource snippet here


  ],
  "outputs": {
    "mynamingdemo":{
      "type": "string",
      "value": "[reference('namingconventionTemplate').outputs.defaultNames.value.apiapp]"
    }
  }
}

It might seem a bit overwelming for a base template, but that part always stays the same. Maybe later on I'll thing of a way to make it even easier.

Adding a resource can now be as simple as:

{
  "type": "Microsoft.Resources/deployments",
  "apiVersion": "2015-01-01",
  "name": "apiappTemplate",
  "properties": {
    "mode": "Incremental",
    "templateLink": {
      "uri": "[reference('configurationTemplate').outputs.apiappTemplateUrl.value]",
      "contentVersion": "1.0.0.0"
    },
    "parameters": {
      "resourceName": {
        "value": "[reference('namingconventionTemplate').outputs.defaultNames.value.apiapp]"
      },
      "environmentName": {
        "value": "[reference('namingconventionTemplate').outputs.envName.value]"
      },
      "appServicePlanName": {
        "value": "[reference('appserviceplanTemplate').outputs.resourceName.value]"
      }
    }
  },
  "dependsOn": [
    "configurationTemplate",
    "namingconventionTemplate",
    "appserviceplanTemplate"
  ]
}

How do I

I created resource snippets you can copy paste (oh dear I just sold my dev soul to the devil again) the snippets of the resource you want in the basetemplate. You don't need to change the resource snipptes. They pick up your naming conventions and you're done.

Download and docs

Download the code from my github