Azure Resource Manager - Nested templates and sharing state

It is obvious to create a single deployment template for deploying a single resource. But the more advanced your deployment scenarios get, the more interesting it becomes to create a decomposed template design. Azure Resource Manager allows you to link templates, also referred to as nested templates. The following guidance helps to decide between a single template or a decomposed nested template design.

  • Create a single template for a single tier application
  • Create a nested template deployment for a multitier application
  • Use nested templates for optional deployment

Nested templates

Within an Azure Resource Manager deployment template you can create a resource of the type "Microsoft.Resources/deployments". This templateLink property in this resource allows you to reference another deployment template.

"resources": [
 {
 "name": "myNestedTemplate",
 "type": "Microsoft.Resources/deployments",
 "apiVersion": "2015-01-01",
 "properties": {
  "mode": "Incremental",
  "templateLink": {
   "uri": "https://raw.githubusercontent.com/marcvaneijk/arm/master/000-patterns/nested-sharing-state/nested/shared1.json",
   "contentVersion": "1.0.0.0"
   }
  }
 }
]

The template that is referenced in the templateLink is deployed as part of the initial (master) deployment. All resources, from both the master and the linked template, will be deployed to the same resource group. You can define multiple resources of the type Microsoft.Resources/deployments in a single deployment template, each pointing to a different nested template. This can be useful if you are deploying a multitier application. Each tier can have its own template that is referenced from the master template.

The dependsOn function allows you to specify the dependencies for each resource referencing and individual nested template. Making smart use of the dependsOn function can drastically decrease the total time needed for the complete deployment (e.g. instead of deploying one tier at a time, deploy the vms in all tiers first and then execute the individual depending tier configurations).

Remove hardcoded URI from resources

In the previous blog Azure Resource Manager - Objects in variables, we discussed grouping variables in objects. These objects can be very useful if you are working with nested templates. Define an object variable in the master azuredeploy.json that contains the absolute Uri of the repository folder. Add an relative path entry in that variable for each nested template you are using in your deployment. This gives a quick overview of the nested templates referenced in your resources. Store all nested templates in folder called nested. The templatelink in the resource combines the absolute Uri with the relative path. When you fork a repository you only need to update the absolute Uri in the variable object of the master azuredeploy.json file.

"variables": {
 "template": {
  "base": "https://raw.githubusercontent.com/marcvaneijk/arm/master/000-foundation/examples/nested-sharing-state/",
  "nested1": "shared1.json",
  "nested2": "shared2.json"
 }
},
"resources": [
 {
 "name": "myNestedTemplate",
 "type": "Microsoft.Resources/deployments",
 "apiVersion": "2015-01-01",
 "properties": {
  "mode": "Incremental",
  "templateLink": {
   "uri": "[concat(variables('template').base, 'nested/', variables('template').nested1)]",
   "contentVersion": "1.0.0.0"
   }
  }
 }
]

Optional nested template deployment

It is possible to deploy a nested template based on parameter input. The parameter input is used to concatenate the relative path to a nested template. Based on the user input a different template is deployed. This enables a conditional nested template deployment. The parameter is used to define the name of the template. Ensure the allowedValues of the input parameter match the names of the nested templates.

"parameters": {
 "optionalResource": {
  "type": "string",
  "defaultValue": "configured",
  "allowedValues": [
   "configured",
   "notconfigured"
  ],
  "metadata": {
   "description": "Based on this value a different nested template is deployed"
  }
 }
},
"variables": {
 "template": {
  "base": "https://raw.githubusercontent.com/marcvaneijk/arm/master/200-nested/201-shared/",
  "optional": "[concat('optional_', parameters('optionalResource'),'.json')]",
 }
},
"resources": [
 {
  "name": "optionalNestedTemplate",
  "type": "Microsoft.Resources/deployments",
  "apiVersion": "2015-01-01",
  "properties": {
   "mode": "Incremental",
   "templateLink": {
   "uri": "[uri(concat(variables('template').base, 'nested/'), variables('template').optional)]",
   "contentVersion": "1.0.0.0"
   }
  }
 }
]

Sharing state

The nested template have their own resources that can require parameters submitted by the tenant at deployment time. The tenant is only presented with the parameters from the master template. Luckily Azure Resource Manager allows you to pass values from the master template to the nested templates. The parameters that you want to pass to a nested template need to be configured at the resource level in the master template.

"resources": [
{
 "name": "myNestedTemplate",
 "type": "Microsoft.Resources/deployments",
 "apiVersion": "2015-01-01",
 "properties": {
  "mode": "Incremental",
  "templateLink": {
   "uri": "[concat(variables('template').base, 'nested/', variables('template').nested1)]",
   "contentVersion": "1.0.0.0"
   },
   "parameters": {
    "adminUsername": {
     "value": "[parameters('adminUsername')]"
    },
    "adminPassword": {
     "value": "[parameters('adminUsername')]"
    }
   }
  }
 }
]

Matching parameters should be defined in the nested template

"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
 "adminUsername": {
  "type": "string"
  },
 "adminPassword": {
  "type": "securestring"
 }
}

Strings

All the nested deployment templates I have seen, share state by specifying individual string values.

Challenges

When the amount of parameters and number of template resources increase it can be very challenging to keep track of the parameters. For a couple of values this isn’t a big issue, but the more parameters you are passing between templates the more complex it becomes to ensure the parameters specified at the resource level in the master template match the parameters in the nested template.

If you add, change or remove a parameter in the master template, you need to update the parameters accordingly in all resources of master template that use the type Microsoft.Resources/deployments and have the related parameter specified. You also need to update the parameters in the nested template and the resources in the nested templates that use the impacted parameter.

Object

As described in the previous blog Azure Resource Manager - Objects in variables, it is also possible to group the parameters in a single variable object. We can then reference the values directly from the object in the nested template without specifying parameters for that in the nested template.

Instead of sharing individual parameters between the templates, we share a single object between the template.

"variables": {
 "template": {
  "base": "https://raw.githubusercontent.com/marcvaneijk/arm/master/000-foundation/examples/nested-sharing-state/",
  "nested1": "shared1.json",
  "nested2": "shared2.json"
 },
 "sharedState": {
  "adminUsername": "[parameters('adminUsername')]",
  "adminPassword": "[parameters('adminPassword')]",
  "domainFqdn": "[parameters('domainFqdn')]",
  "vmPrefix": "myVm",
  "storagePrefix": "storage"
 }
},
"resources": [
 {
  "name": "myNestedTemplate",
  "type": "Microsoft.Resources/deployments",
  "apiVersion": "2015-01-01",
  "properties": {
   "mode": "Incremental",
   "templateLink": {
   "uri": "[concat(variables('template').base, 'nested/', variables('template').nested2)]",
   "contentVersion": "1.0.0.0"
   },
   "parameters": {
    "apiVersion": {
     "value": "[variables('sharedState')]"
    }
   }
  }
 }
]

In the nested template you only need to create a single parameter of the type object.

"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
 "sharedSate": {
  "type": "object"
 }
}

In the nested template you can now use the same format used for object variables to reference individual values from the parameter object, by using the format "[parameters('sharedState').subvalue]".

Pros

  • Simpler to keep track of changes in the state that is shared between the linked templates.
  • You can define a single or just a few parameter objects to pass parameters and variables.
  • You can define all variables and parameters in the master template and still keep it easy to share state

Cons

  • Nested templates from a decomposed template design can be deploy seperately. If you’re using a single parameter of the type object it will not be very easy to specify the value for the object at deployment time.

Example

You can find a full deployable example template design, with both sharing state through individual parameters as sharing state through an object my in GitHub repository here:

https://github.com/marcvaneijk/arm/tree/master/000-patterns/nested-sharing-state