Deploying Bicep Template using Azure DevOps
Tags: azure, devops, tutorial
Published: 2023-02-23 02:39:05

In this short post we are going to look at automating the deployment of a Bicep template using Azure DevOps. Additionally, we are going to look at how you can convert an ARM template to Bicep. 

You can find all the files used in this post on my GitHub [here](https://github.com/lakovmartin/azure/tree/main/arm/resource_group).

Azure Bicep is almost an abstraction of ARM (JSON) templates specifically developed for Azure and hence they are not cloud agnostic. The main difference is that Bicep is a lot more human readable and generally you need fewer lines of code to achieve the same result through the use of simple syntax. 

You can read more about Bicep [here](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/overview?tabs=bicep)

Some pre-requisites before we get started: 

- Azure CLI - Download here https://learn.microsoft.com/en-us/cli/azure/install-azure-cli
- Azure DevOps Access
- Azure Subscription

## Converting an ARM Template to Bicep

With the Azure CLI Microsoft provides functionality for you to decompile an ARM/JSON template to Bicep. This is extremely useful if you have existing deployments, and you want to start managing them with Bicep. 

For this tutorial we are going to perform a simple resource group deployment. Let's look at our ARM template. It's split into a `template.json` file which has the definition of what we want to deploy and a `parameters.json` where we provide specific named variables required by the `template.json`. 

It's worth noting that the parameters file does not need to be converted. This remains in a JSON format. 

**Template File**

```json
{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "contentVersion": "1.0.0.1",
  "parameters": {
      "rgName": {
          "type": "string"
      },
      "rgLocation": {
          "type": "string"
      },
      "tags": {
          "type": "object",
          "defaultValue": {}
      }
  },
  "variables": {},
  "resources": [
      {
          "type": "Microsoft.Resources/resourceGroups",
          "apiVersion": "2018-05-01",
          "location": "[parameters('rgLocation')]",
          "name": "[parameters('rgName')]",
          "properties": {},
          "tags": "[parameters('tags')]"
      }
  ],
  "outputs": {}
}
```
**Parameter File**

```json
{
    "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "rgName": {
            "value": "arm-template-test-rg"
        },
        "rgLocation": {
            "value": "australiaeast"
        },
        "tags": {
            "value": {}
        }
    }
}
```
**Let's convert our template to Bicep**

1: Make sure you have the Azure CLI installed on your local machine. Navigate to the folder containing your ARM template and run the following command: 

```
az bicep decompile --file template.json
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00acshtz5zl0srhe4igs.png)

Once the process completes, you will notice another file has been added to the directory called `template.bicep`. This is our converted template.

2: Let's review the code for our new bicep file: 

```
targetScope = 'subscription'
param rgName string
param rgLocation string
param tags object = {
}

resource rg 'Microsoft.Resources/resourceGroups@2018-05-01' = {
  location: rgLocation
  name: rgName
  properties: {
  }
  tags: tags
}
```
Here you can notice straight away how the template is much shorter and more concise. We went from 28 lines of code to 13. It's worth noting that the conversion for bigger files can have some issue with naming formats so you may need to do some polishing before it's done. We can use our existing JSON parameter file to supply the values required. 

## Deploy the template manually

Let's deploy the template manually to make sure it works using the Azure CLI. 

1: Run the following Azure CLI command. Make sure to replace the template and parameter file name if you used something different in the previous step.

```powershell
az deployment sub create `
  --name TestDeployment `
  --location australiaeast `
  --template-file template.bicep `
  --parameters parameters.json
```

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g12s2ejujmsgm909cbsh.png)

We can now see the RG is ready in the Azure portal. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/txmkmkebx8a448zbr72m.png)

Let's delete the RG and deploy the same templates using Azure DevOps for full automation. 

## Deploy the template using Azure DevOps

1: Create a new pipeline from Azure DevOps by navigating to `Pipelines `and then `New pipelines`


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/goqq0omrl9le6xew9qr1.png)

2: Select `Azure Repos Git` for your code repository

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zrotwn3tqgsutd3d9x8.png)

3: Select `Starter pipeline`

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/94imjmxrjj6bgcixw8yn.png)

4: Paste the following code:

```yaml
trigger:
- master

name: Deploy Bicep files


variables:
  vmImageName: 'ubuntu-latest'

  azureServiceConnection: 'devops-poc3-deploy-spi'
  location: 'australiaeast'
  templateFile: 'bicep/azure_resource_group/template.bicep'
  templateParameterFile: 'bicep/azure_resource_group/parameters.json'
pool:
  vmImage: $(vmImageName)

steps:
- task: AzureResourceManagerTemplateDeployment@3
  inputs:
    deploymentScope: 'Subscription'
    azureResourceManagerConnection: '$(azureServiceConnection)'
    action: 'Create Or Update Resource Group'
    location: '$(location)'
    templateLocation: 'Linked artifact'
    csmFile: '$(Build.SourcesDirectory)/$(templateFile)'
    csmParametersFile: '$(Build.SourcesDirectory)/$(templateParameterFile)'
    deploymentMode: 'Incremental'
    deploymentName: 'DeployPipelineTemplate'
```
In this scenario I am providing the template and parameter file as a linked artifact in Azure DevOps by specifying the location in the following parameters `csmFile` & `csmParametersFile`. The pipeline also has a trigger for CD so every merge to the main/master branch will re-run it. Since we are using incremental deployments this is pretty safe to do.

5: Save the pipeline and run it. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/klchwfjzp9kirqtgzchv.png)

We have now deployed the resource group through Azure DevOps using Bicep.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgqeo7eofu2s8iquokgr.png)

In the next post we are going to look at how to perform some automated tests on our Bicep deployment using the same pipelines including code linting, deployment validation, what-if checks and approval gates.