How to govern Azure Bicep with template specs?

Azure DevOps Infrastructure-as-code Azure Bicep 💪

Table of contents:

Last week I was setting up a static web site using Azure DevOps and Bicep consuming templates from git. This approach might not be the best option for a large environment with multiple teams as we’ll run into several challenges trying to share and (re)use the templates. Fortunately, Microsoft has a better place for the template. It’s called Azure Resource Manager template specs, which is a regular resource with type Microsoft.Resources/templateSpecs that has several benefits including ability to store templates with different versions (both ARM and Bicep are supported), manage access through Azure RBAC, template’s consumer does not need full access to it and can deploy resource just passing parameters.

Azure resource governance with project Bicep and Template Specs schema

The process:

  1. Platform team plans what has to be provisioned.
  2. Platform team creates IaC templates to provision cloud resources (exact list of resources depends on the nature of the application / system).
  3. Repo is a central place for template sources that the platform team uses.
  4. Pipeline runs to validate new version of the template and publishes it to Template spec.
  5. Consumer (in this case Team Blue) passes parameters and provisions resource using Template spec.
  6. Consumer provides feedback to the platform team (i.e. template has to be extended with additional parameters or new modules has to be added) and the process begins another round.

In this repo I have some code for both parties: platform team and consumer team.

2├── consumer-team-blue
3│   ├── parameters-team-blue.json
4│   └──
5├── template-spec-arm
6└── template-spec-bicep
7    ├──
8    └── main.bicep

Publish Template Spec #

As I’ve mentioned, Azure Template Spec is just a regular resource, we can also see it in the Azure Portal like below:

Azure Template Spec resource

I am going to use the ACR resource as an example (this is defined in my main.bicep, see below). It requires only acrName and acrSku as entry parameters (also acrSku as default value Basic in case if this parameter has not been passed).

 3@description('Name of the azure container registry (must be globally unique)')
 4param acrName string
 6@description('Enable an admin user that has push/pull permission to the registry.')
 7param acrAdminUserEnabled bool = false
 9@description('Location for all resources.')
10param location string = resourceGroup().location
13  'Basic'
14  'Standard'
15  'Premium'
17@description('Tier of your Azure Container Registry.')
18param acrSku string = 'Basic'
20// azure container registry
21resource acr 'Microsoft.ContainerRegistry/registries@2019-12-01-preview' = {
22  name: acrName
23  location: location
24  tags: {
25    displayName: 'Container Registry'
26    'container.registry': acrName
27  }
28  sku: {
29    name: acrSku
30  }
31  properties: {
32    adminUserEnabled: acrAdminUserEnabled
33  }
36output acrLoginServer string =

Now what we want to do is validate it and if it’s okay - publish. This is how I publish the template to the Spec.

 3export TEMPLATE_SPEC="WeekendSprints-TemplateSpec-ACR"
 4export TEMPLATE_SPEC_DISPLAY_NAME="Weekend Sprints Template Spec ACR"
 5export TEMPLATE_SPEC_DESCRIPTION="Weekends Sprints Template Spec ACR Basic tier"
 6export RESOURCE_GROUP="WeekendSprints-TemplateSpec-RG"
 7export LOCATION="westeurope"
 8export VERSION="1.0.0"
10echo "Deploying template spec to ... "
11echo "Resource group:   $RESOURCE_GROUP"
12echo "Location:         $LOCATION"
13echo "Version:          $VERSION"
15az group create -n $RESOURCE_GROUP -l $LOCATION
17echo "Resource group $RESOURCE_GROUP has been created/updated"
19az ts create \
20    --name $TEMPLATE_SPEC \
21    --display-name "$TEMPLATE_SPEC_DISPLAY_NAME" \
22    --description "$TEMPLATE_SPEC_DESCRIPTION" \
23    --version $VERSION \
24    --tags BU=Finance Environment=Production \
25    --resource-group $RESOURCE_GROUP \
26    --location $LOCATION \
27    --template-file "./main.bicep" \
28    --yes \
29    --query 'id' -o json

While doing this in unattended way (pipeline/action) we can prepend validation task az deployment group validate ... to fail the step in case of errors in the template or lack of parameters as I explained in the previous post.

Azure Portal - Template Spec

Now via Access Control (IAM) we can define who can access the template (builtin Reader role should be sufficient to consume it). We can deploy this via the portal as highlighted on the picture above, but we would like a better process). Let’s take a look at the consumer’s part.

Consume Template Spec #

As it has been said earlier a consumer needs to supply two parameters. One of the option to do so is to define parameters in the file

 2    "$schema": "",
 3    "contentVersion": "",
 4    "parameters": {
 5      "acrName":{
 6          "value": "pardus"
 7      },
 8      "acrSku": {
 9          "value": "Standard"
10      }
11    }
12  }

and consume like this:

 2echo "Provisioning environments..."
10RGP=$(az group create -l $LOCATION -n $RESOURCE_GROUP)
11echo "Resource group $RESOURCE_GROUP has been created/updated"
13ACR_TS_ID=$(az ts show --resource-group $TS_RG --name $TS_NAME --version $VERSION --query 'id' -o json)
15az deployment group what-if \
16  --resource-group $RESOURCE_GROUP \
17  --template-spec $ACR_TS_ID \
18  --parameters "./parameters-team-blue.json"
20az deployment group validate \
21  --resource-group $RESOURCE_GROUP \
22  --template-spec $ACR_TS_ID \
23  --parameters "./parameters-team-blue.json"
25az deployment group create \
26  --resource-group $RESOURCE_GROUP \
27  --template-spec $ACR_TS_ID \
28  --parameters "./parameters-team-blue.json"

There it is:

Azure portal - ACR Azure portal - ACR deployment

Alternatively a parameter can be passed via environment variable and defined in AZ CLI like explained here.

Do you have similar process in place? I would love to hear from you in the comments below. Thank you for reading! 💪

comments powered by Disqus