Comparing JSON and Bicep for templates
This article compares Bicep syntax with JSON syntax for Azure Resource Manager templates (ARM templates). In most cases, Bicep provides syntax that is less verbose than the equivalent in JSON.
If you're familiar with using JSON to develop ARM templates, use the following examples to learn about the equivalent syntax for Bicep.
Compare complete files
The Bicep Playground lets you view Bicep and equivalent JSON side by side. You can compare the implementations of the same infrastructure.
For example, you can view the file to deploy a SQL server and database. The Bicep is about half the size of the ARM template.
Expressions
To author an expression:
func()
"[func()]"
Parameters
To declare a parameter with a default value:
param orgName string = 'Contoso'
"parameters": {
"orgName": {
"type": "string",
"defaultValue": "Contoso"
}
}
To get a parameter value, use the name you defined:
name: orgName
"name": "[parameters('orgName')]"
Variables
To declare a variable:
var description = 'example value'
"variables": {
"description": "example value"
}
To get a variable value, use the name you defined:
workloadSetting: description
"workloadSetting": "[variables('description')]"
Strings
To concatenate strings:
name: '${namePrefix}-vm'
"name": "[concat(parameters('namePrefix'), '-vm')]"
Logical operators
To return the logical AND:
isMonday && isNovember
[and(parameter('isMonday'), parameter('isNovember'))]
To conditionally set a value:
isMonday ? 'valueIfTrue' : 'valueIfFalse'
[if(parameters('isMonday'), 'valueIfTrue', 'valueIfFalse')]
Deployment scope
To set the target scope of the deployment:
targetScope = 'subscription'
"$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#"
Resources
To declare a resource:
resource virtualMachine 'Microsoft.Compute/virtualMachines@2024-03-01' = {
...
}
"resources": [
{
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2024-03-01",
...
}
]
To conditionally deploy a resource:
resource virtualMachine 'Microsoft.Compute/virtualMachines@2024-03-01' = if(deployVM) {
...
}
"resources": [
{
"condition": "[parameters('deployVM')]",
"type": "Microsoft.Compute/virtualMachines",
"apiVersion": "2024-03-01",
...
}
]
To set a resource property:
sku: '2016-Datacenter'
"sku": "2016-Datacenter",
To get the resource ID of a resource in the template:
nic1.id
[resourceId('Microsoft.Network/networkInterfaces', variables('nic1Name'))]
Loops
To iterate over items in an array or count:
[for storageName in storageAccountNames: {
...
}]
"copy": {
"name": "storagecopy",
"count": "[length(parameters('storageAccountNames'))]"
},
...
Resource dependencies
For Bicep, you can set an explicit dependency but this approach isn't recommended. Instead, rely on implicit dependencies. An implicit dependency is created when one resource declaration references the identifier of another resource.
The following shows a network interface with an implicit dependency on a network security group. It references the network security group with netSecurityGroup.id
.
resource netSecurityGroup 'Microsoft.Network/networkSecurityGroups@2023-11-01' = {
...
}
resource nic1 'Microsoft.Network/networkInterfaces@2023-11-01' = {
name: nic1Name
location: location
properties: {
...
networkSecurityGroup: {
id: netSecurityGroup.id
}
}
}
If you must set an explicit dependence, use:
dependsOn: [ storageAccount ]
"dependsOn": ["[resourceId('Microsoft.Storage/storageAccounts', 'parameters('storageAccountName'))]"]
Reference resources
To get a property from a resource in the template:
storageAccount.properties.primaryEndpoints.blob
[reference(resourceId('Microsoft.Storage/storageAccounts', variables('storageAccountName'))).primaryEndpoints.blob]
To get a property from an existing resource that isn't deployed in the template:
resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' existing = {
name: storageAccountName
}
// use later in template as often as needed
storageAccount.properties.primaryEndpoints.blob
// required every time the property is needed
"[reference(resourceId('Microsoft.Storage/storageAccounts/', parameters('storageAccountName')), '2019-06-01').primaryEndpoints.blob]"
In Bicep, use the nested accessor (::
) to get a property on a resource nested within a parent resource:
VNet1::Subnet1.properties.addressPrefix
For JSON, use reference function:
[reference(resourceId('Microsoft.Network/virtualNetworks/subnets', variables('subnetName'))).properties.addressPrefix]
Outputs
To output a property from a resource in the template:
output hostname string = publicIP.properties.dnsSettings.fqdn
"outputs": {
"hostname": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))).dnsSettings.fqdn]"
},
}
To conditionally output a value:
output hostname string = condition ? publicIP.properties.dnsSettings.fqdn : ''
"outputs": {
"hostname": {
"condition": "[variables('condition')]",
"type": "string",
"value": "[reference(resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))).dnsSettings.fqdn]"
}
}
The Bicep ternary operator is the equivalent to the if function in an ARM template JSON, not the condition property. The ternary syntax has to evaluate to one value or the other. If the condition is false in the preceding samples, Bicep outputs a hostname with an empty string, but JSON outputs no values.
Code reuse
To separate a solution into multiple files:
- For Bicep, use modules.
- For ARM templates, use linked templates.
Next steps
- For information about the Bicep, see Bicep quickstart.
- To learn about converting templates between the languages, see Converting ARM templates between JSON and Bicep.