Deliver ARM templates at speed using Template Specs in Azure
Tags: azure, devops, iac, templatespecs
Published: 2021-02-17 06:27:36

A template spec is used to store your Azure Resource Manager (ARM) templates securely in Azure environment. **Microsoft.Resources/templateSpecs** is the resource type for template specs.

Using template specs you can share ARM templates with users in your organization. They also support versioning which we will see later in this post.

## Why to use template specs?

Suppose you are working on Infrastructure development using ARM templates in your organization. There are several issues when trying to share these templates with other teams. If the templates are stored in a repository or storage account, users won't be able to access it unless they have required permissions. Even if permission is granted users will deploy using their own local version of templates which can eventually become outdated as you keep on updating the template.

Template Specs in Azure are protected by Azure Role-Based Access Control (RBAC). So you can securely store the templates in a resource group and control access using RBAC in Azure itself. When the reference architectures in your organization change over time, you can update the template specs with newer versions.

## Create a new template spec with linked template

When you create a template spec, you just pass the main template file to the CLI commands. If there are linked templates referenced inside the main template file, those will be packaged together with the main template in a single template spec resource.

Create a main template named `azuredeploy.json` with the following JSON in your working directory:

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "appServicePlanName": {
            "type": "string",
            "metadata": {
                "description": "Enter a name for app service plan"
            }
        },
        "appServiceName": {
            "type": "string",
            "metadata": {
                "description": "Enter a name for app service"
            }
        }
    },
    "functions": [],
    "variables": {},
    "resources": [
    {
        "name": "webappDeployment",
        "type": "Microsoft.Resources/deployments",
        "apiVersion": "2020-06-01",
        "properties": {
            "mode": "Incremental",
            "templateLink": {
                "relativePath": "artifacts/webapp.json"
            },
            "parameters": {
                "appServicePlanName": {
                    "value": "[parameters('appServicePlanName')]"
                },
                "appServiceName": {
                    "value": "[parameters('appServiceName')]"
                }
            }
        }
    }
    ],
    "outputs": {}
}
```

Note the deployment resource in above template references a linked template `webapp.json`. We will create this linked template next.

In your working directory create a new directory named `artifacts` and create a template named `webapp.json` inside the directory with the following JSON:

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "appServicePlanName": {
            "type": "string",
            "metadata": {
                "description": "Name of app service plan"
            }
        },
        "appServiceName": {
            "type": "string",
            "metadata": {
                "description": "Name of app service"
            }
        }
    },
    "functions": [],
    "variables": {},
    "resources": [
        {
            "name": "[parameters('appServicePlanName')]",
            "type": "Microsoft.Web/serverfarms",
            "apiVersion": "2019-08-01",
            "kind": "app",
            "location": "[resourceGroup().location]",
            "sku": {
                "name": "S1"
            }
        },
        {
            "name": "[parameters('appServiceName')]",
            "type": "Microsoft.Web/sites",
            "apiVersion": "2019-08-01",
            "kind": "app",
            "location": "[resourceGroup().location]",
            "dependsOn": [
                "[resourceId('Microsoft.Web/serverfarms', parameters('appServicePlanName'))]"
            ],
            "properties": {
                "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', 'appServicePlan1')]"
            }
        }
    ],
    "outputs": {}
}
```

## Deploy the template spec

To deploy the template spec, you can use various Azure tools like PowerShell, Azure CLI, Azure portal, REST, and other supported SDKs and clients. We will use Powershell to deploy our template spec.

Open a Powershell terminal in your working directory and run the following Powershell cmdlets to create a new resource group:

```powershell
$rg = "TemplateSpecsRG"

New-AzResourceGroup `
  -Name $rg `
  -Location eastus
```

Then run the `New-AzTemplateSpec` cmdlet to create a new template spec based on the above templates:

```powershell
New-AzTemplateSpec `
  -Name webappSpec `
  -Version "1.0" `
  -ResourceGroupName $rg `
  -Location eastus `
  -TemplateFile "azuredeploy.json"
```

When the cmdlet executes successfully, you can view the deployed spec using `Get-AzTemplateSpec` cmdlet:

```powershell
> Get-AzTemplateSpec -ResourceGroupName $rg -Name webappSpec


Id                    : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/TemplateSpecsRG/providers/Microsoft.Resou
                        rces/templateSpecs/webappSpec
Name                  : webappSpec
ResourceGroupName     : TemplateSpecsRG
SubscriptionId        : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Location              : eastus
Versions              : 1.0
CreationTime(UTC)     : 2/17/2021 5:42:57 AM
LastModifiedTime(UTC) : 2/17/2021 5:43:02 AM
```

In the portal too, you can navigate to the resource group and view the template spec resource:

![Template Spec resource in the portal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nml5mwftpoht721wbcqv.png)

When you go to the template spec resource screen, you can find that it contains a main template and the linked template that we deployed. Note that the version you entered is also displayed as **Latest Version**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8qckp1zfut0ytlkcfg90.png)

You can also drill down on the templates and see the content in the portal itself.

## Modify the template spec

Now suppose the architecture gets updated and a new storage account needs to be added along with the web app. We will modify the existing template spec to add a new storage account as a linked template.

Modify the `azuredeploy.json` file and add a new deployment resource. The modified template should like as below:

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "appServicePlanName": {
            "type": "string",
            "metadata": {
                "description": "Enter a name for app service plan"
            }
        },
        "appServiceName": {
            "type": "string",
            "metadata": {
                "description": "Enter a name for app service"
            }
        },
        "storageAccountName": {
            "type": "string",
            "metadata": {
                "description": "Enter a name for storage account"
            }
        },
        "storageAccountType": {
            "type": "string",
            "defaultValue": "Standard_LRS",
            "metadata": {
                "description": "Specify the storage account type"
            }
        }
    },
    "functions": [],
    "variables": {},
    "resources": [
        {
            "name": "webappDeployment",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2020-06-01",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "relativePath": "artifacts/webapp.json"
                },
                "parameters": {
                    "appServicePlanName": {
                        "value": "[parameters('appServicePlanName')]"
                    },
                    "appServiceName": {
                        "value": "[parameters('appServiceName')]"
                    }
                }
            }
        },
        {
            "name": "storageAccountDeployment",
            "type": "Microsoft.Resources/deployments",
            "apiVersion": "2020-06-01",
            "properties": {
                "mode": "Incremental",
                "templateLink": {
                    "relativePath": "artifacts/storageAccount.json"
                },
                "parameters": {
                    "storageAccountName": {
                        "value": "[parameters('storageAccountName')]"
                    },
                    "storageAccountType": {
                        "value": "[parameters('storageAccountType')]"
                    }
                }
            }
        }
    ],
    "outputs": {}
}
```

Create a new template `storageAccount.json` in the `artifacts` directory with the following JSON:

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "storageAccountName": {
            "type": "string",
            "metadata": {
            "description": "Storage Account Name"
            }
        },
        "storageAccountType": {
            "type": "string",
            "defaultValue": "Standard_LRS",
            "allowedValues": [
            "Standard_LRS",
            "Standard_GRS",
            "Standard_ZRS",
            "Premium_LRS"
            ],
            "metadata": {
            "description": "Storage Account type"
            }
        },
        "location": {
            "type": "string",
            "defaultValue": "[resourceGroup().location]",
            "metadata": {
            "description": "Location for all resources."
            }
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Storage/storageAccounts",
            "apiVersion": "2019-04-01",
            "name": "[parameters('storageAccountName')]",
            "location": "[parameters('location')]",
            "sku": {
            "name": "[parameters('storageAccountType')]"
            },
            "kind": "StorageV2",
            "properties": {}
        }
    ],
    "outputs": {}
}
```

Now re-deploy the template spec with a newer `Version`:
```powershell
New-AzTemplateSpec `
  -Name webappSpec `
  -Version "1.1" `
  -ResourceGroupName $rg `
  -Location eastus `
  -TemplateFile "azuredeploy.json"
```

If you enter the same version as the previous one, it will overwrite the template spec. You can either update the existing version or publish a new version as needed.

Now when you run the `Get-AzTemplateSpec` cmdlet, you can see two versions for your template spec in the output:

```powershell
> Get-AzTemplateSpec -ResourceGroupName $rg -Name webappSpec


Id                    : /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/TemplateSpecsRG/providers/Microsoft.Resou
                        rces/templateSpecs/webappSpec
Name                  : webappSpec
ResourceGroupName     : TemplateSpecsRG
SubscriptionId        : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
Location              : eastus
Versions              : {1.0, 1.1}
CreationTime(UTC)     : 2/17/2021 5:42:57 AM
LastModifiedTime(UTC) : 2/17/2021 6:00:07 AM
```

After the cmdlet executes, verify that the **Latest Version** is updated in the portal and a new artifact `storageAccount.json` is added to the template spec

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/clbfshqlvbaqbagv5f7p.png)

This way you can develop and share ARM templates quickly with the users in your organization.

You can also refer to the below Youtube video on [Microsoft Azure](https://www.youtube.com/channel/UC0m-80FnNY2Qb7obvTL_2fA) channel.

{% youtube l-MtfehL80k %}
Start leveraging template specs right away!