In-Depth Look at Azure Bicep Syntax

In-Depth Look at Azure Bicep Syntax

If you have not checked out my other series, “Getting Started with Bicep” then I would highly suggest starting there so you can familiarize yourself with Azure Bicep including some basic syntax and how to write your first template. This article will walk you through more advanced syntax topics including output variables, loops and more!


Comments in code help you or others figure out what something may do or why it’s there in the first place. While it does not replace full documentation, it is a great way to get a picture of what your code does.

Single Line Comment

You can comment a single line of bicep code by adding ‘//’ to it. In the example below you can see my comment added to the tagging resource that we will go over next!

Multi-line Comments

Instead of adding ‘//’ to every line you want to comment on, you can span across multiple lines using ‘/*’ to the beginning of the multi-line comment, and ‘/*’ to the end of the multi-line comment.


With Bicep, you may find that one resource you are deploying must be deployed after another one. When you run into a situation like this you can use the dependsOn property so the resource is never created prior to the other. It’s important to remember that even if you declare a resource later on in your template, it may still be deployed prior to something declared earlier.

In the example below, I am creating another Storage Account below. I use dependsOn on the 2nd Storage Account by referencing the first Storage Account’s symbolic name, ‘storageaccount’.

Note: With some resources, Bicep is smart enough to know that child resources need to be deployed behind its parent. This occurs when it notices that a declaration contains a reference to a previously declared resource. (ie a storage account and a storage table, when creating the storage table you will need to reference the storage account in the tables name by calling the storage accounts symbolic name)

Output Variables

Output variables allow you to gather information from your deployment and output them so you can review them quickly and easily. Output variables should be at the end/bottom of your bicep deployment script. In my example below I am creating a new output variable named allowPublicAccess (make sure to pick a name that tells you what the value means from a quick glance) that will display a boolean value (true/false) on if my newly deployed storage account allows blob public access.

output allowPublicAccess bool =

If I then go into the Azure Portal, select my Resource Group and click ‘Deployments’ I can review the output variables and values from my deployment.


Tags consist of a name and a value pair. Example: you can have a tag with a name Department and possible values of IT, Accounting, HelpDesk, and so on. Both the name and the value of tags are case sensitive.

There are some limitations on tags:

  • Not every azure resource supports tags. most do, but you can verify what resources support tags here
  • Tags per subscription1: 50
  • Unique tag calculations per subscription1: 80,000
  • Tag key length: 512 characters
  • Tag value length: 256 characters

1You can apply up to 50 tags directly to a subscription. However, the subscription can contain an unlimited number of tags that are applied to resource groups and resources within the subscription. The number of tags per resource or resource group is limited to 50. Resource Manager returns a list of unique tag name and values in the subscription only when the number of tags is 80,000 or less. You still can find a resource by tag when the number exceeds 80,000.

Using the same Bicep code example from the Getting Started series, you can see I added a new parameter object called resourceTag. Then in my storage account resource I added the object to the tags property.

And when I deploy it, it sees that I already have the storage account deployed in my Azure environment but it is missing the defined tag. So it will make a change to that resource and tag it.


Simple Loops

Loops are an important item with Bicep, they enable you to easily create same resources without having to copy and paste the declaration over and over. In my example below I created a new Bicep template to deploy 3 Storage Accounts. On line 6 I created an array of my Storage Account names and then line 12 I am iterating through that list in the For statement. ‘Think of it as foreach item in the array do the following’

The new symbolic name is created on the for line called “storageAccountName” which only exists within my loop. I use that symbolic name on line 13 which gives my new Storage Account its proper name.

When I begin the deployment of this Bicep template I can see that it will correctly create my three new Storage Accounts.

Viewing the deployment details, I can see my newly created Storage Accounts!

Multi-Property Loops

In the first example, we used an array to set a single property, ‘name’. What if we wanted to set not only name but also the access tier of each new Storage Account?

In the example below I removed the accessTier param and modified the storageAccountNames array to include a new property: accessTier. The on line 28, I change it to look at the symbolic name storageAccountName and the property ‘accessTier’.

After deploying this to my test tenant, I can see that the dev Storage Account changed from cool storage to hot!

Bicep Functions

Azure Bicep supports a wide array of functions. You can review this list that will show you all the different functions that are available. We will go over some of the more common ones.

Any Function

Bicep supports a function called any() to resolve type errors in the Bicep type system. You use this function when the format of the value you provide doesn’t match what the type system expects. For example, in the below snippet, on line 13, the diskSizeGB is expecting a int value but I gave it a string value (defined on line 4). By using the any() function it will ignore the error and simply convert the value type from string to int.

This function doesn’t exist in the Azure Resource Manager template runtime. It’s only used by Bicep and isn’t emitted in the JSON for the built template.

And during my deployment I can see that I was not presented with any errors or warnings and will create the resource in Azure.

Scope Functions


The resourceGroup() function gets the information of the Resource Group where you are deploying your resources too during runtime. If you wanted to change the location param to automatically deploy your resources to your Resource Group’s location, you can use resourceGroup().location

In the example below you can see I changed the param location (line 5) from ‘northcentralus’ to whatever location my Resource Group has.

String Functions

String Interpolation

String concatenation is the process of joining two or more strings into a single string. ARM used the concat() function to perform this, and in Bicep we use string interpolation. String interpolation string interpolation (or variable interpolation, variable substitution, or variable expansion) is the process of evaluating a string literal containing one or more placeholders, yielding a result in which the placeholders are replaced with their corresponding values.

In the example below I declared a new parameter named ‘namePrefix’ that contains the value, ‘prd’ which is short for production. Usually I would create resources with a prefix followed by a hyphen but Azure Storage Accounts do not support this naming so I have to exclude the hyphen. I then modified line 4 so the storageAccountName param would include the value from the namePrefix param. During deployment it will name the Storage Account ‘prdbicepstorage630618’.

When doing string interpolation, you need to enclose it with {} brackets and add the dollar sign ($).


toLower() converts the specified string to lower case. This can be handy if you are getting the value at runtime and you want to ensure whatever the individual puts is converted to all lowercase.

In the example below, I am creating the Storage Account name on line 4 and it contains some uppercase characters. Line 13 where I am declaring the name and value, I use toLower() to convert the entire string to all lowercase. The resulting name will be prdbicepstorage630618.

Note: Since Storage Accounts in Azure cannot have upper case characters in the name, it may be a good idea to use toLower() to ensure names remain valid.


toUpper() converts the specified string to upper case. This can be handy if you are getting the value at runtime and you want to ensure whatever the individual puts is converted to all uppercase.

In the example below, I am creating the Storage Account name on line 4 and it contains some uppercase characters and lowercase characters. Line 13 where I am declaring the name and value, I use toUpper() to convert the entire string to all lowercase. The resulting name will be PRDBICEPSTORAGE630618.

Note: Storage Account name cannot contain uppercase characters so this will result in an error.

Logical Operators

The logical operators evaluate boolean values, return non-null values, or evaluate a conditional expression.

Operator Name
&& And
|| Or
! Not
?? Coalesce
? : Conditional expression

Conditional Expression

Evaluates a condition and returns a value whether the condition is true or false. Think of this as ‘if true do the first thing, if false do the 2nd thing’

In the example below I added a new boolean (true/false) param called hotAccessTier. On line 23 in the new properties portion I am saying if the param hotAccessTier is true, then set the storage account’s access tier to ‘Hot’, otherwise it should be set to ‘Cold’.


Parameter Decorators

Parameter decorators are in the format @expression and are placed above the parameter’s declaration. You can mark a parameter as secure (@secure), specify allowed values (@allowed), set the minimum (@minLength) and maximum length (@maxLength) for a string, set the minimum (@minValue) and maximum value (@maxValue) for an integer, and provide a description (@description) of the parameter.

The following table describes the available decorators and how to use them.

Decorator Apply to Argument Description
allowed all array Allowed values for the parameter. Use this decorator to make sure the user provides correct values.
description all string Text that explains how to use the parameter. The description is displayed to users through the portal.
maxLength array, string int The maximum length for string and array parameters. The value is inclusive.
maxValue int int The maximum value for the integer parameter. This value is inclusive.
metadata all object Custom properties to apply to the parameter. Can include a description property that is equivalent to the description decorator.
minLength array, string int The minimum length for string and array parameters. The value is inclusive.
minValue int int The minimum value for the integer parameter. This value is inclusive.
secure string, object none Marks the parameter as secure. The value for a secure parameter isn’t saved to the deployment history and isn’t logged. For more information, see Secure strings and objects.


The allowed decorator specifies allowed values for an item. Looking at the earlier example we can make it so only the ‘Hot’ and ‘Cool’ values are available and nothing else. In the example below I changed the param from a boolean to a string and I declared that only Hot and Cool values are allowed by using the @allowed param decorator above. Since I did not set a default by declaring a value for the param (param accessTier string = ‘Cool’) then during deployment it will ask me what the access tier should be.


Looking at the above example, it may be unclear what you are setting, especially if you name your parameters poorly. Using the Description param decorator I can provide a much more friendlier message. The description value is only shown during deployment if you enter ‘?’ for help.


A Storage Account name is restricted to a max 24 characters. We can ensure the maxLength of the name does not exceed 24 characters and cause the deployment to fail by using the maxLength decorator. In my example below, I added the @maxLength decorator on line 4 and made the potential name on line 5 larger than 24 characters. You can also view the failure during a potential deployment as well.


Storage Account names also have a minimum value of 3 characters. We can use the minLength decorator to ensure a value below that number will fail validation.


The @metadata decorator can be used to specify any other custom properties that describe the parameter or its significance. You can add any custom property to @metadata decorator; however, for description, the value will be passed as if you defined it using the @description decorator.


minValue is an integer parameter decorator. Meaning instead of setting a minimum amount of characters in a string, it sets a minimum number value.

In the example below I added a new property (line 38-40): ‘keyExpirationPeriodInDays,’ which sets your storage account’s access key rotation value. I set a minvalue on line 25 of the parameter.


maxValue does the opposite and sets a maximum int value that the parameter can have. In my example below I set the maxValue at 90 days.


The @secure parameter decorator marks your parameter as secure and prevents the parameter’s value from being saved to the deployment history and logs. It will not transform any plaintext value in the deployment file itself to anything but plaintext. If you have a parameter that uses @secure it should not have any default value.

In my example below I modified the namePrefix param for my storage account from a hardcoded value of ‘prd’ to null. I also used the @secure parameter decorator to indicate that the parameter value should be secured. When I run the deployment I am prompted for the value, which is not shown even on the console.

@secure is used more when you need to enter a secret value, like a password or key.


Leave a Reply

Your email address will not be published. Required fields are marked *