The more I use Bicep, the more I love it. This is what ARM Templates should have been. When it comes to IaC, I usually use Terraform. It's the IaC tool we used at my last gig and I like that it has support for multiple clouds.
However, I've recently changed jobs and I'm finding that I'm using ARM templates more. With this in mind, I've been wanting to learn Bicep and use it in my own personal projects so when the day comes that I have to convert ARM templates to Bicep code, I'll be prepared 😂
Coming back to this article, I'm working on a personal health application that has a bunch of APIs (Built using Azure Functions ⚡) that interacts with my data. Ideally, I'd like to integrate this within Azure API Management. I deploy these APIs using Azure DevOps so to be consistent, I want to deploy APIM using IaC via Azure DevOps.
I'm going to show you how we can provision an Azure API Management instance using Bicep code and then deploy it using Azure DevOps.
One thing to note before we get started is that of the time of writing, there are no officially supported tasks for Bicep in Azure DevOps. For Terraform and ARM templates, we can use tasks in DevOps to deploy our infrastructure. For this article, I've used some AZ CLI tasks to build and deploy my Bicep templates.
So if you're reading this in a future where we can use officially supported Bicep tasks in DevOps, just keep this caveat in mind 😊
Bicep is a domain-specific language that uses declarative syntax to deploy Azure Resources. When we wrote ARM templates, we were essentially writing JSON to deploy resources to Azure. The syntax for this could get a little complex and for fancy stuff, we would need to write complicated expressions to get it working.
Bicep Lang reduces that complexity significantly. Bicep is a transparent abstraction over ARM templates and when we deploy Bicep templates, it comes with a CLI that transpiles the Bicep file into ARM template JSON.
As of v0.3, Bicep is supported by Microsoft and has 100% parity with ARM templates, meaning that you can start using it for production workloads!
If you want to learn more about Bicep, you can check out the documentation here: https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/
API Management (APIM) allows us to create consistent API gateways for back-end services. Using APIM, we can publish APIs and make them available for external and internal developers to consume.
APIM is made up of:
If you want to dive a bit deeper into APIM, check out the documentation: https://docs.microsoft.com/en-us/azure/api-management/
Let's start writing our Bicep code! 💪 The best tool for writing Bicep code is Visual Studio Code. There's also an awesome extension that you can download that will help validate your Bicep code and provide intellisense: https://github.com/Azure/bicep/blob/main/docs/installing.md#install-the-bicep-vs-code-extension
For this tutorial, I'm not going to focus too much on the complicated aspects of APIM. I just want to provision a simple configuration to get started with.
From what I can see from the docs, it looks like I'll need the following properties:
You can see the full reference for the APIM template here: https://docs.microsoft.com/en-us/azure/templates/microsoft.apimanagement/2019-01-01/service?tabs=bicep
With the above properties in mind, we can write the following Bicep code:
param apimName string
param apimLocation string
param publisherName string
param publisherEmail string
resource myhealthapim 'Microsoft.ApiManagement/service@2019-12-01' = {
name: apimName
location: apimLocation
sku: {
name: 'Developer'
capacity: 1
}
properties: {
publisherEmail: publisherEmail
publisherName: publisherName
}
}
As you can see, I've used some parameters for some of the configuration values for my APIM instance. Let's discuss what these are in Bicep.
In Bicep, we can use parameters to provide information to our Bicep templates when we deploy them. This allows us to make our Bicep templates reusable.
In the code above, we declared our parameters using the param keyword. Here's an example:
param parameterName string
We can provide parameters through files. Parameter files are created using JSON. If we are deploying resources to multiple environments, we can create a parameter file for each environment and provided different values depending on the environment.
For this tutorial, I've created the following file.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"apimName": {
"value": "myhealthapiportal"
},
"apimLocation": {
"value": "australiaeast"
},
"publisherName": {
"value": "MyHealth API Portal"
},
"publisherEmail": {
"value": "[email protected]"
}
}
}
If you want to learn how parameters in Bicep, check out this Learn course:
In order to deploy our Bicep template, we'll need a Service Connection in DevOps that's authorized to deploy resources to Azure.
If you need to set this up, check out the following documentation: https://docs.microsoft.com/en-us/azure/devops/pipelines/library/service-endpoints?view=azure-devops&tabs=yaml#create-a-service-connection
Now that I have my Bicep template and parameter file, we can start to create our release pipeline.
I've kinda shot myself in the foot here. I have a uber-repo that I use for all of my IaC code that I've written to deploy resources to Azure. So for this tutorial, I'll be using the classic Release pipeline rather than YAML.
For this release, I used the following tasks:
Let's go through each task.
For our Copy Files task, I'm just copying the files from my GitHub artifact into the Default Working Directory on my build agent.
We'll use files in our target directory to run Bicep commands from.
Let's take a look at our first Azure CLI task:
Here, we are running the following az cli command
az bicep build --file "$env:SYSTEM_DEFAULTWORKINGDIRECTORY/main.bicep"
Here, we are building our bicep file. We can do this to build the ARM template that would be created by the bicep file, which could be used to run automated tests against, or for adding it to template specification, which can be shared with other users in our team.
To reference our Build file in the command, I'm using our System Default Working Directory as an environment. This is the folder that we copied our files to before. We can access variables in our release pipelines to transport and exchange data throughout our release pipelines: https://docs.microsoft.com/en-us/azure/devops/pipelines/release/variables?view=azure-devops&tabs=batch#default-variables
Now let's move onto our final task, where we will be deploying our Bicep template:
In this AZ CLI task, we're running the following commands
az group create -l australiaeast -n myhealthapiportal-rg
az deployment group create --template-file "$env:SYSTEM_DEFAULTWORKINGDIRECTORY/main.bicep" --parameters "$env:SYSTEM_DEFAULTWORKINGDIRECTORY/main.parameters.json" --resource-group myhealthapiportal-rg
In our first command, we're creating a resource group in Azure called myhealthapiportal-rg to deploy our APIM instance to.
In our second command, we are creating a deployment at the subscription scope, using our main.bicep file as the template that we want to deploy and our main.parameters.json file as the parameters we will use for this deployment. I've also hard coded the resource group that I created earlier that I want to deploy our APIM resource to.
With this all set up, we can now run our release pipeline and it should deploy our APIM instance to Azure
Verifying that we deployed to Azure:
This took me a lot of goes, mainly messing about with the AZ CLI commands and getting them to point at our files.
Another thing to note is that I managed to deploy APIM the first time, but the deployment task took over an hour and failed, even though the deployment was successful when I went into my Azure portal.
Thanks for reading this article. At the time of writing, there are no official tasks for Bicep in Azure DevOps, so what I've done here to get this going is a bit of a hack, but it works.
If you want to learn more about Bicep, I recommend checking out the following resources from Microsoft Learn:
Hopefully you found this article useful! As always, if you have any questions, feel free to comment below or ask me on Twitter!
Happy Coding! 💻👨💻👩💻