Introduction to Azure ARM Template Specs
Tags: azure, devops, iac
Published: 2021-02-02 21:13:37

In a previous post, [Linked templates in ARM template]( https://dev.to/omiossec/linked-templates-in-arm-templates-4ha9), I demonstrated how to modularize templates by using the template linking technique. The goal is to create several little ARM templates and a central template orchestrating the deployment of these child templates. 
There are many limitations to this technique. First, you need to host your child template in a globally available location like a GitHub repository or an Azure Storage Account with a SAS key. Second, you cannot use the Azure Resource Manager role-based control with your linked template. 

Version management is the third limitation. With ARM templates in external storage, version control is managed by the ContentVersion property. It's a manual process and you need to make sure that both the calling and child templates use the same version. 

Template Specs address these limitations. Template Specs is an Azure resource and not external storage, like any other resource, you can add roles and authorizations. 
A Template Spec is an object where you can store an ARM JSON template and its different versions. When you want to deploy a version of this template you can call it in an ARM template by using a reference to the object and the version you want to deploy. 

To create a Template spec, you need a JSON template file and a resource group. You will have to give a name to the template version you will upload. 
You will need to use the latest version of the AZ PowerShell module or AZ CLI. 

For example, if you have this template, named vnet.json, to create a VNET resource

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "vnetName": {
            "type": "string"
        },
        "vnetPrefix": {
            "type": "string"
        },
        "subnetName": {
            "type": "string"
        },
        "subnetPrefix": {
            "type": "string"
        }
    },
    "variables": {},
    "resources": [
        {
            "name": "[parameters('vnetName')]",
            "type": "Microsoft.Network/virtualNetworks",
            "apiVersion": "2019-11-01",
            "location": "[resourceGroup().location]",
            "properties": {
                "addressSpace": {
                    "addressPrefixes": [
                        "[parameters('vnetPrefix')]"
                    ]
                },
                "subnets": [
                    {
                        "name": "[parameters('subnetName')]",
                        "properties": {
                            "addressPrefix": "[parameters('subnetPrefix')]"
                        }
                    }
                ]
            }
        }
    ],
    "outputs": {}
}
```

For this template, I choose "vnet-1-subnet.0.1" as the version name. To create the Template Spec, you will need a resource group, a location, and a name for the Template Spec. You will asol need to provide template data, your JSON data, to create the object.
You can also add tags to the Template Spec resource, just like any other resource in Azure.  You can also add a Description, a version description, and a Displayname.

To create a TemplateSpec for the vnet.json file.

```powershell
New-AzTemplateSpec -Name VNET -ResourceGroupName 01-TPLSPECS  -Version "vnet-1-subnet.0.1"  -location westeurope -TemplateFile ./storage.json -tags @{"project"="vnet design"} -Description "store VNET ARM Template" -VersionDescription "Vnet with one subnet"  -DisplayName "Vnet deployment" 
```

Imagine that you want to store another version with two subnets instead of one. After the modification in your template file, you can run the same command with the version and versionDescription modified

```powershell
New-AzTemplateSpec -Name VNET -ResourceGroupName 01-TPLSPECS  -Version "vnet-2-subnet.0.1"  -location westeurope -TemplateFile ./storage.json -tags @{"project"="vnet design"} -Description "store VNET ARM Template" -VersionDescription "Vnet with two subnet"  -DisplayName "Vnet deployment" 
```

As an Azure resource, you can also create a Template Spec via ARM template. There are two ARM template resources the Microsoft.Resources/templateSpecs and its child resource Microsoft.Resources/templateSpecs/versions. 
Take a look at this template. 

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "templateSpecsName": {
            "type": "String"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Resources/templateSpecs",
            "apiVersion": "2019-06-01-preview",
            "name": "[parameters('templateSpecsName')]",
            "location": "westeurope",
            "tags": {},
            "properties": {
            }
        },
        {
            "type": "Microsoft.Resources/templateSpecs/versions",
            "apiVersion": "2019-06-01-preview",
            "name": "[concat(parameters('templateSpecsName'), '/1.0.0.b')]",
            "location": "westeurope",
            "dependsOn": [
                "[resourceId('Microsoft.Resources/templateSpecs', parameters('templateSpecsName'))]"
            ],
            "properties": {
                "artifacts": [],
                "template": {
                        
                        "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
                        "contentVersion": "1.0.0.0",
                        "parameters": {},
                        "functions": [],
                        "variables": {},
                        "resources": [
                            {
                                "name": "udsiuisdudisudsi",
                                "type": "Microsoft.Storage/storageAccounts",
                                "apiVersion": "2019-06-01",
                                "location": "[resourceGroup().location]",
                                "kind": "StorageV2",
                                "sku": {
                                    "name": "Premium_LRS",
                                    "tier": "Premium"
                                }
                            }
                        ],
                        "outputs": {}
                    }
                }
            }
        
    ]
}
```

You can create the template version inside Microsoft.Resources/templateSpecs/versions


As an Azure resource, you can assign roles to your Template Spec object. Image a situation where you have a team who develop templates and a service principal used to deploy the same templates. You can add the contributor roles to the dev team and reader role to the service principal. 

```powershell
New-AzRoleAssignment -ObjectId (Get-AzADGroup -DisplayName 'arm-team').id  -RoleDefinitionName "Contributor" -Scope "/subscriptions/XXXXX-6200-4226-bac3-XXXXXXXX/resourceGroups/01-TPLSPECS/providers/Microsoft.Resources/templateSpecs/testarm01"

New-AzRoleAssignment -ObjectId (Get-AzADServicePrincipal -DisplayName 'spArmTemplate').id -RoleDefinitionName "Reader" -Scope "/subscriptions/XXXXX-6200-4226-bac3-XXXXXXXX/resourceGroups/01-TPLSPECS/providers/Microsoft.Resources/templateSpecs/testarm01"
```

These two PowerShell commands allow the member of the arm-team to create, update and delete versions in the testarm01 Template Spec object and allow the service principal spArmTemplate to retrieve data from the same object. 

To get information about the TemplateSpec object you can use the Get-AzTemplateSpec cmdlet. 

```powershell
Get-AzTemplateSpec -Name VNET -ResourceGroupName 01-TPLSPECS
```

It returns a PSTemplateSpec object 

```
Id                    : /subscriptions/XXXXXXXX/resourceGroups/01-TPLSPECS/providers/Microsoft.Resources/templateSpecs/VNET
Name                  : VNET
DisplayName           : Vnet deployment
ResourceGroupName     : 01-TPLSPECS
SubscriptionId        : XXXXXXXXXXX
Location              : westeurope
Tags                  : {[project, vnet design]}
Description           : store VNET ARM Template
Versions              : {vnet-1-subnet.0.1, vnet-2-subnet.0.1}
CreationTime(UTC)     : 01/29/2021 21:49:33
LastModifiedTime(UTC) : 01/29/2021 22:16:51
```

It’s also possible to export a template from a Template Spec version by using the Export-AzTemplateSpec cmdlet. You need to provide the name of the Template Spec and the version along with a local folder. The cmdlet will create the folder, if it doesn't exist, and export the JSON template under the name <TemplateSpecName>.<VersionName>.json

```powershell
Export-AzTemplateSpec -Name VNET -ResourceGroupName 01-TPLSPECS  -Version "vnet-1-subnet.0.1" -OutputFolder v1
```

Once your template versions are available to be deployed and uploaded in a Template Spec object you can start using them in your deployments. Calling a Template Spec use de same resource type you use with the traditional template linking technic; Microsoft.Resource/deployment. But, be sure to use the latest API version. Instead of using a URI for the templateLink property, you will need to use an ID with the reference of the Template Spec version object you need to use. 

In my example, if I want to use the "vnet-1-subnet.0.1" version of the VNET Template Spec in the 01-TPLSPECS resource group. The reference will be: 

"[resourceId('01-TPLSPECS', 'Microsoft.Resources/templateSpecs/versions', 'VNET', 'vnet-1-subnet.0.1')]"
 
The template with all parameters 

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {},
    "variables": {},
    "resources": [
        {
            "apiVersion": "2020-06-01",
            "name": "demo-tpl-spec01",
            "type": "Microsoft.Resources/deployments",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "id": "[resourceId('01-TPLSPECS', 'Microsoft.Resources/templateSpecs/versions', 'VNET', 'vnet-1-subnet.0.1')]"
                },
                "parameters": {
                    "vnetName": {
                        "value": "vnet01"
                    },
                    "vnetPrefix": {
                        "value": "10.0.0.0/21"
                    },
                    "subnetName": {
                        "value": "subnet01"
                    },
                    "subnetPrefix": {
                        "value": "10.0.0.0/24"
                    }
                }
            }
        }
    ],
    "outputs": {}
}
```

Template Spec offers a new way to manage linked templates. Instead of relying on external services like GitHub or other Internet-facing storage solutions or an Azure storage account, it integrates an Azure resource. Using an Azure resource to manage linked templates as a huge advantage, a unified access management system, Azure role-based access control.

You can store multiple versions of one template inside a Template Spec and create a Template Spec for each template type. You can also use one template spec per project and host any type of template if you want.

To know more about ARM Template Specs visit the [Microsoft Doc Page](https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/template-specs?WT.mc_id=AZ-MVP-5003620&tabs=azure-powershell)