Nested ARM Templates: Modularity and Reuse
Perfect for complex templates or those that you want to reuse, see how to create nested ARM templates for your Azure deployments.
Join the DZone community and get the full member experience.
Join For FreeMost example ARM templates use a single JSON file to contain all of the deployment details in a single place. This works fine for smaller deployments, but once you start doing larger deployments, working in teams, or wanting to reuse parts of your deployment templates, then you really need to start looking at nested templates.
Nesting templates describes the process of calling an ARM template from inside another. In this way, you can separate your logic into multiple files, call these files as required, and pass parameters and results between templates. Nesting has been supported in ARM from the beginning, and these nested templates are just treated like another resource.
Modularization
Splitting your template over multiple files can help with readability and organization, but one of the big benefits is modularization and reusability. If there are resources you regularly deploy in a specific configuration, you can wrap them up in their own template and reuse them as required throughout your different solutions.
If you take a look at Microsoft's Best Practices for ARM Templates, it contains the diagram below with a recommended approach to organizing your templates.
This technique allows for reusable templates shared between deployments and provides a shared resource template, which contains resources that are shared between multiple deployments — for example, a virtual network that is deployed and managed by a central function and referenced by this shared resource template.
Nesting Templates
There are two ways to include a nested template:
- Inline in your main template
- As separate JSON files called from your main template.
Inline Nesting
Inline nesting is really not something that I would recommend doing except where you have a very simple set of resources in your inline template. Inline testing essentially involves having a second template included inside your first like below:
"resources": [
{
"apiVersion": "2017-05-10",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"resourceGroup": "crossSubscriptionDeployment",
"subscriptionId": "623d50f1-4fa8-4e46-a967-a9214aed43ab",
"properties": {
"mode": "Incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {},
"variables": {},
"resources": [
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[parameters('StorageAccountName2')]",
"apiVersion": "2015-06-15",
"location": "West US",
"properties": {
"accountType": "Standard_LRS"
}
}
]
},
"parameters": {}
}
},
{
"type": "Microsoft.Storage/storageAccounts",
"name": "[parameters('StorageAccountName1')]",
"apiVersion": "2015-06-15",
"location": "West US",
"properties": {
"accountType": "Standard_LRS"
}
}
]
In this example, we have a resource of type Microsoft.Resources/deployments. This is the resource we use to call a nested template. This resource has a property of “template”, and inside this, we are putting the whole second template. As you can see, this has no benefit for the complexity of the file or for modularizing our work. The only reason you would do this is if something you are doing requires a nested template and you only have a few resources in that template. In this example, we are deploying some resources into a second resource group and subscription. Cross-RG and subscription deployments require the use of a nested template.
Nested Files
Nested files are the way I recommend you go. Here, we will reference a second JSON file from our main template. The nested template, the one you are calling from the main template, is no different from a normal template. You just define your resources as normal. Any information you require from the main template should be set up as parameters.
In your main template, you will again use a Microsoft.Resources/deployments resource, but instead of the template property, we will use templateLink:
"variables": {
"templatelink": "https://raw.githubusercontent.com/sam-cogan/Demos/master/DemoExamples/newStorageAccount.json"
},
"resources": [
{
"apiVersion": "2015-01-01",
"name": "nestedTemplate",
"type": "Microsoft.Resources/deployments",
"properties": {
"mode": "incremental",
"templateLink": {
"uri": "[variables('templatelink')]",
"contentVersion": "1.0.0.0"
},
"parameters": {
"StorageAccountName": {
"value": "[parameters('StorageAccountName')]"
}
}
}
}
]
As you can see, we are using the templateLink property to pass in a link to my second template file. One thing to be aware of here: The second template file needs to be reachable at deployment time, so this is running inside Azure. As such, it can’t just exist on my local machine — it needs to be hosted somewhere accessible. In this example, I am using GitHub, but you can use any HTTP service. If you want to keep the template secure, the best option is to use Azure Storage with a SAS token.
You can also see here that I am passing in a parameter (StorageAccountName). This will be received by my nested template as a parameter and used in the standard way. Obviously, as you are passing the parameters through from our main template, we need to make sure that all parameter data we need exists in our main template or is, in turn, passed into it as a parameter.
That’s all there is to it. When I want to deploy the template I just run a new-azurermresourcegroupdeployment from PowerShell, or whatever method you usually run the main template with, and provide parameters. It will handle running the nested templates.
Outputs
All ARM templates have an outputs section. In single file templates, this is mainly used to pass values back to the caller (PowerShell, etc.). With nested templates, we can use this to pass values back to the calling, top-level template, which can then be used in this template or even passed to other nested templates.
Creating an output is the same as in a normal template. Here we are passing the full details of the storage account created in the nested template.
"outputs": {
"storageAccountInfo": {
"value": "[reference(concat('Microsoft.Storage/storageAccounts/', parameters('StorageAccountName')),providers('Microsoft.Storage', 'storageAccounts').apiVersions[0])]",
"type" : "object"
}
In the main template, we would then use this by referencing the nested template step and variable name:
"storageAccountInfo": { "value": "[reference('nestedTemplate').outputs.storageAccountInfo.value]" },
Next Steps
Using nested templates, you can become much more organized with your templates and start reusing your common templates throughout your different projects, even providing a library of building blocks that you can share with colleagues
If you are building complex sets of templates, I would very much recommend reading Microsoft’s best practice guidance on designing complex templates.
The examples used in this article, plus more, can be found in my GitHub Repo.
Published at DZone with permission of Sam Cogan, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments