It is Biceps πŸ’ͺ Day! Flexing an ARM Template to deploy Azure Sentinel 🏹

Roberto Rodriguez
Open Threat Research
9 min readSep 9, 2020

Ever since I joined the Microsoft Threat Intelligence Center (MSTIC) R&D team, I have been learning about Azure Resource Manager (ARM) templates to deploy several detection research environments as code. It has been a great journey learning about the syntax and format, and even when some might not like writing templates in JSON format to deploy resources in Azure, I actually like it πŸ˜†! However, it is a little hard for me sometimes to teach or walk someone through the templates I write because of the JSON format.

Recently, I heard about a new project from Microsoft Azure named Bicep, and even though is still in early development stages (alpha), it looks promising. It provides a cleaner syntax and better support for modularity. Therefore, I decided to write a short post sharing some of the steps I took to translate an ARM template to its Bicep format to learn more about it.

I highly recommend to build something with a tool you want to learn about

In this post, I will show you how to install, set up and write a bicep template by using the following ARM template as a reference. I wrote that template, a few months ago, to deploy Azure Sentinel in a lab environment and used it as a foundation to develop the following resources:

What is Bicep?

Bicep is a Domain Specific Language (DSL) for deploying Azure resources declaratively. It aims to drastically simplify the authoring experience with a cleaner syntax and better support for modularity and code re-use. Bicep is a transparent abstraction over ARM and ARM templates, which means anything that can be done in an ARM Template can be done in bicep (outside of temporary known limitations).

Setting Up a Bicep Dev Environment

There are very detailed examples to set up your environment in the projects docs depending on your operating system. You will need the following binaries from the project releases:

  • Bicep CLI (required) β€” Compiles bicep files into ARM templates.
  • Bicep VS Code Ext (optional) β€” Authoring support, intellisense, validation.

Install Latest Bicep CLI (macOS)

# Fetch the latest Bicep CLI binary
> curl -Lo bicep https://github.com/Azure/bicep/releases/latest/download/bicep-osx-x64
# Mark it as executable
> chmod +x ./bicep
# Add Gatekeeper exception (requires admin)
> sudo spctl --add ./bicep
# Add bicep to your PATH (requires admin)
> sudo mv ./bicep /usr/local/bin/bicep

Validate installation:

> bicep --help
Bicep CLI version 0.1.1-alpha (427d5e7abe)
Usage:
bicep build [options] [<files>...]
Builds one or more .bicep files
Arguments:
<files> The list of one or more .bicep files to build
Options:
--stdout Prints all output to stdout instead of corresponding files
bicep [options]
Options:
--version -v Shows bicep version information
--help -h Shows this usage information

Install Latest Bicep VS Code Extension (macOS)

# Fetch the latest Bicep VSCode extension
> curl -Lo vscode-bicep.vsix https://github.com/Azure/bicep/releases/latest/download/vscode-bicep.vsix

In the next step, we need to use the code command from VS Code to install the extension. If you have not installed yet, do it via VS Code:

  • In VS Code, open the Command Palette (F1 or ⇧+⌘+P on Mac)
  • Type shell command to find the Shell Command: Install 'code' command in PATH command as shown below:

Click on Shell Command: Install 'code' command in PATH

validate if it is installed or not:

> code -hUsage: code [options][paths...]To read from stdin, append '-' (e.g. 'ps aux | grep code | code -')Options
-d --diff <file> <file> Compare two files with each other.
-a --add <folder> Add folder(s) to the last active window.
-g --goto <file:line[:character]> Open a file at the path on the specified line and character position.
-n --new-window Force to open a new window.
-r --reuse-window Force to open a file or folder in an already opened window.
--folder-uri <uri> Opens a window with given folder uri(s)
--file-uri <uri> Opens a window with given file uri(s)
-w --wait Wait for the files to be closed before returning.
--locale <locale> The locale to use (e.g. en-US or zh-TW).
--user-data-dir <dir> Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code.
-h --help Print usage.

Next, install the Bicep extension:

> code --install-extension vscode-bicep.vsix
Installing extensions...
Extension 'vscode-bicep.vsix' was successfully installed.
# Clean up the file
> rm vscode-bicep.vsix

If you create an empty file called main.bicep and open it in VS Code, you should see the extension working:

Compile Bicep File -> ARM Template (Quick Test)

This is also part of the instructions in the project and is used as a basic example to understand how an empty bicep file can actually provide a basic ARM template with no resources in it, but with all the main sections of a classic ARM template to use. Here is where it starts to make sense! πŸ˜‰

bicep build main.bicep

Azure Sentinel Template (ARM -> Bicep -> ARM)

We are now ready to learn how we can translate/transform an ARM template to Bicep format. Once again, the template deploys an Azure Sentinel SIEM.

https://github.com/OTRF/Blacksmith/blob/master/templates/azure/Log-Analytics-Workspace-Sentinel/azuredeploy.json

In order to deploy an Azure Sentinel SIEM, we need to deploy and enable the following resources:

Log Analytics Workspace Resource (ARM Format)

The log Analytics workspace resource syntax used in ARM templates can be found under the Microsoft.OperationalInsights resource types as part of the Microsoft.OperationalInsights/workspaces.

{
"name": "string",
"type": "Microsoft.OperationalInsights/workspaces",
"apiVersion": "2015-11-01-preview",
"location": "string",
"tags": {},
"properties": {
"sku": {
"name": "string"
},
"retentionInDays": "integer"
},
"resources": []
}

Log Analytics Workspace Resource (Bicep Format)

πŸ”₯ I started writing my first lines of code in bicep, and the bicep extension tab completion features helped me a lot to get familiarized with the syntax:

According to the docs, the resource declaration has four main components:

  • resource keyword
  • symbolic name (Identifier) - this is an identifier for referencing the resource throughout the bicep file. It is not the name of the resource I want to deploy (i.e. Microsoft.OperationalInsights/workspaces )
  • type (Microsoft.Provider/Type@Version) - composed of the resource provider (i.e.Microsoft.OperationalInsights), resource type (i.e.workspaces), and apiVersion (i.e.2020-03-01-preview).
  • properties (everything inside = {...}) - these are the specific properties you would like to specify for the given resource type.

My Bicep template then should look like this:

We are going to add parameters and variables in a little bit to make it more dynamic. This is my first bicep code πŸ˜ƒ 🍻 🎊

Azure Sentinel Solution (ARM Format)

The Azure Sentinel solution needs to be enabled on the top of the Log Analytics workspace. The resource syntax used in ARM templates can be found under the Microsoft.OperationsManagement resource types as part of the Microsoft.OperationsManagement/solutions .

{
"name": "string",
"type": "Microsoft.OperationsManagement/solutions",
"apiVersion": "2015-11-01-preview",
"location": "string",
"tags": {},
"plan": {
"name": "string",
"publisher": "string",
"promotionCode": "string",
"product": "string"
},
"properties": {
"workspaceResourceId": "string",
"containedResources": [
"string"
],
"referencedResources": [
"string"
]
}
}

Azure Sentinel Solution (Bicep Format)

Following the same idea as the previous resource, this is the bicep format:

I added a section named plan . This is used in the solution resource. I was not sure if it was available, but after a search through the repo, I found out it wasπŸ˜ƒ. Also, you must be wondering what I meant with Implicit Dependency .

According to the docs, the order of the deployment of each resource can be defined via the following dependencies:

  • Explicit Dependency: It is declared via the dependsOn property within the resource declaration. This it what you would use in an ARM template.
  • Implicit Dependency: It is created when one resource declaration references the identifier of another resource declaration in an expression. This is new and I like it. In my Azure Sentinel bicep template, I used the identifier workspace in the properties and plan section to retrieve the name and resource id of the workspace resource. Therefore, a dependency is defined by default πŸ˜ƒ.

Parameters (ARM Format)

I like to make my templates dynamic enough so that a user can customize it a little bit. In my current ARM template, this is how parameters are defined:

Parameters (Bicep Format)

In Bicep format, the parameters can be defined the following way:

Variables (ARM Format)

One of the things that I like to automate is the uniqueness of a name applied to resources that require a global scope such as a Log Analytics Workspace. I use variables to do that. For my ARM template, I had it defined utilizing built-in functions and parameters as shown below:

"variables": {
"uniqueWorkspace": "[concat('log-', parameters('workspaceName'), uniquestring(resourceGroup().id, parameters('utcValue')))]"
},

Variables (Bicep Format)

In Bicep format, it would look something like this

var uniqueWorkspace = concat('log-', workspaceName, uniqueString(resourceGroup().id, utcValue))

Outputs (ARM Format)

I use outputs in my ARM templates to be able to use those values and pass them over to another template. It helps me a lot to create sequences of templates with dynamic inputs (i.e a resource ID will always change)

Outputs (Bicep Format)

Final Bicep Template

I just needed to implement the parameters and variables defined in bicep across the two resources I defined earlier and πŸ’₯. This is my first full bicep template to deploy azure resources. In this case, an Azure Sentinel SIEM πŸ’ͺ

Build Template Bicep -> ARM (Validation) βœ…

bicep build main.bicep

In the image below, the template on the left is the original ARM template and the one on the right is the one created after running Bicep . One thing I learned right away, from the output, is the consistency in the order of the parameter properties . I need to improve that on my templates πŸ˜† ❀️

Bicep is in alpha at the moment. Therefore, whenever Bicep is ready to be used in production, I will write my templates in Bicep and then export them to ARM format to deploy them (I assume Azure will come up with a feature to deploy Bicep templates directly to the Azure Resource Service 🍻)

Deploying ARM Template (Validation) βœ…

This is a little bit out of the scope for this post, but just to validate that it works, you can easily deploy it via Azure PowerShell, Azure CLI or the Azure portal. I usually deploy mine via the Azure CLI or an Aure Deploy button πŸ˜‰. The version of Azure CLI used while writing this post was 2.11.1.

Azure CLI (Create Resource Group)

You do not have to create a resource group, but for a lab environment and isolate it from other resources, I run the following command:

az group create -n my-rg -l eastus

Azure CLI (Deploy ARM Template)

az deployment group create -f ./main.json -g my-rgPlease provide string value for 'workspaceName' (? for help): AzureSentinel
- Running ..

Checking Progress ⏳

Check your Azure Sentinel 🏹

Azure Portal > Azure Sentinel > {WorkspaceName}

That’s it! I hope you enjoyed this post. Iwanted to learn a little bit more about Bicep, and translating one of my ARM templates to it was very helpful! In addition, the documentation provided in the GitHub repository of the project is awesome and easy to follow! I cannot wait for this project to be ready for other more complex scenarios that require the current features addressed in the limitations section of the project.

Azure Sentinel ARM: https://github.com/OTRF/Blacksmith/blob/master/templates/azure/Log-Analytics-Workspace-Sentinel/main.json

Azure Sentinel Bicep: https://github.com/OTRF/Blacksmith/blob/master/templates/azure/Log-Analytics-Workspace-Sentinel/main.bicep

References

--

--