- Version: 1.0.0
- Author:
- Nathan Nellans
- Email: [email protected]
- Web:
Important
This is an advanced guide and assumes you already know the basics of Bicep. Think of this more like an advanced cheat sheet. I went through various sources, captured any notes that I felt were important, and organized them into the README file you see here. If you are new to Bicep, then I would suggest going through the Microsoft Docs or doing a couple Microsoft Learn courses first.
Warning
This is a live document. Some of the sections are still a work in progress. I will be continually updating it over time.
Note
Here is a link to my own Bicep Deployment Series which goes over the various nuances of deploying Bicep files.
Bicep files use a .bicep
file extension and are written using their own custom domain-specific language (DSL).
If you're accustomed to Terraform, you will see that Bicep works differently when it comes to deploying code. Terraform will combine every .tf
file in the current directory and deploy them all at the same time. Whereas Bicep will deploy one main .bicep
file per deployment. It is suggested to name this file main.bicep
If you are storing parameters values in a separate parameters JSON file, it is common practice to use the name of the Bicep file and just add the word "parameters" like shown below. If you are using the newer Bicep parameter format, then just use the name of the Bicep file with the extension of .bicepparam
. Support for .bicepparam
files requires Bicep v0.18.4 or later.
Bicep file: main.bicep
JSON Parameter file: main.parameters.json
Bicep Parameter file: main.bicepparam
Here are the major sections of a bicep file. This is also the recommended order in which they should appear
- These are simple key/value pairs that can contain any information you want to include
- Some good examples are a description, author, creation date, etc.
- These are optional
metadata description = 'This file deploys 12 resources'
metadata author = '[email protected]'
- You can only have 1
targetScope
entry at the top of your file - It can be set to 1 of 4 options, all listed below
- This specifies the level at which all of the resources in this Bicep file will be deployed (however, you can get around this by using Modules, more on that later)
- This line is optional. If you omit it, the default value of
resourceGroup
is used
targetScope = 'resourceGroup'
targetScope = 'subscription'
targetScope = 'managementGroup'
targetScope = 'tenant'
- Parameters are for values that will change/vary between different deployments
- Each Parameter must be set to one of the supported Data Types (see below)
- Optionally, you can use
=
to set a default value for the Parameter- The default value can use expressions, but it can NOT use the
reference
orlist
functions
- The default value can use expressions, but it can NOT use the
// Defining Parameters
param myParameter1 int
param myParameter2 array
param myParameter3 string = 'default Value'
// Using Parameters
// Just use the name of the Parameter
resource exampleStorageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
name: myParameter1
location: myParameter2
}
- Decorators are placed directly above the parameter definition
- You can use more than one decorator for each parameter definition
- It's good practice to specify the
minLength
andmaxLength
decorators for parameters that control resource naming. These limitations help avoid errors later during deployment- For integers you can specify
minValue
andmaxValue
decorators, instead
- For integers you can specify
- It's good practice to specify the
description
decorator for all of your parameters. Try to make them helpful - The
allowed
decorator can be used to provide allowed values in an array. If the value doesn't match, then the deployment fails- Use this sparingly, as Azure makes changes frequently to things like SKUs and sizes, so you don't want to have an allowed list that is out of date
- The
metadata
decorator is an object that can contain properties of any name and type. Use this for info that you don't want to put into thedescription
decorator
@minLength(1)
@maxLength(80)
@description('Provide a name for the VirtualWAN resource')
@allowed([
'option1'
'option2'
])
param myParameter4 string
@minValue(1)
@maxValue(20)
@metadata({
key1: 'value1'
key2: 'value2'
})
param myParameter5 int
Bicep data types include: array, bool, int, object, string (plus secureObject and secureString)
- Arrays use square brackets:
[ ]
// Multi-line arrays (use line breaks to separate values)
param someName array = [
'one'
'two'
'three'
]
// Single-line arrays (use commas to separate values)
// Requires Bicep 0.7.4 or newer
param someName array = [ 'one', 'two', 'three' ]
- In single-line, a comma after the last value is supported, but not required
- The data types in an array do NOT have to match, as each item is represented by the 'any' type
- Bicep arrays are zero-based, so using the example above
someName[0] = 'one'
- Simply use either
true
orfalse
with no quotation marks
param exampleBoolParameter bool = false
- A simple, whole number with no quotation marks
- In Bicep, these are 64-bit Integers
- Bicep does NOT support floating point, decimal, or binary yet
param exampleIntParameter int = 1200
- Objects use braces / curly brackets:
{ }
// Multi-line objects (use line breaks to separate pairs)
param someName object = {
key: 'value'
key: 'value'
}
// Single-line objects (use commas to separate pairs)
// Requires Bicep 0.7.4 or newer
param someName object = { key: 'value', key: 'value' }
- For single-line, a comma after the last pair is supported, but not required
- Each property of the Object can be of any type
- Optionally, if the key contains special characters, you can enclose the key in single quotes
- You can use a period to access values, so
exampleObjectParameter.key2 = true
- You can also use brackets to access values, so
exampleObjectParameter['key4'].keyA = 'value2'
- Complex object example:
param exampleObjectParameter object = { key1: 'value' key2: true 'special.key3': 150 key4: { keyA: 'value2' } }
- Bicep uses single quotes for single-line strings
- All Unicode characters with code points between 0 and 10FFFF (both inclusive) are allowed
- Escape Characters:
- Use
\\
for\
- Use
\'
for'
(single quote) - Use
\n
for line feed (LF) - Use
\r
for carriage return (CR) - Use
\t
for tab - Use
\u{###}
for unicode characters - Use
\$
for$
(dollar sign) (this is only needed if you must type${
and you're NOT using interpolation)
- Use
param exampleStringParameter string = 'example string'
- Opening sequence is
'''
(three single quotes) - Closing sequence is
'''
(three single quotes) - Everything in-between is read verbatim
- Escape characters are NOT not possible inside the string
- You can NOT include
'''
(three single quotes) inside your multi-line string - Bicep does NOT support interpolation inside your multi-line string yet
param exampleMultilineParameter string = '''
this
is
an
idented
example
'''
- Just add the
@secure()
decorator before your Object or String parameter - The value is not saved to the deployment history and is not logged
- Make sure you use this parameter in a location that expects a secure value, for example Tag values are stored in plain text, so do not use a secureString parameter in a Tag
- Since these are secure, do NOT set a default value, as that will be stored in code
@secure()
param exampleSecureObjectParameter object
@secure()
param exampleSecureStringParameter string
- Starting with Bicep v0.21.1, you can create your own user-defined types
// This custom type defines a string, and sets 3 allowed values
type myCustomType1 = 'azure' | 'gcp' | 'aws'
// To define an array put [] at the end
// This custom type defines an array of strings
type myCustomType2 = string[]
// This custom type defines an array of strings, and only allows certain values in the array
type myCustomType3 = ('one' | 'two' | 'three')[]
// This custom type defines an object of mixed types
type myCustomType4 = {
name: string
age: int
gender: string? // The ? at the end marks this as optional
}
// Then, in your parameter, use this custom type instead of the standard types like `string`, `bool`, etc.
param someParamName myCustomType3
// Option 2, simply define the custom type inline in your parameter (this defines an array of objects)
param someParamName {
name: string
age: int
}[]
- Instead of embedding complex expressions directly into resource properties, use variables to contain the expressions
- This approach makes your Bicep file easier to read and understand. It avoids cluttering your resource definitions with logic
- When you define a variable, the data type isn't needed. Variables infer the type from the resolved value
- The value of the variable can use all available expressions, including the
reference
orlist
functions
// Defining Variables
var myVariable1 = 'some value for the var'
// Using Variables
// Just use the name of the Variable
resource exampleStorageAccount 'Microsoft.Storage/storageAccounts@2021-04-01' = {
name: myVariable1
}
- It's a good idea to use a recent API version for each resource. New features in Azure services are sometimes available only in newer API versions
- When possible, avoid using the
reference
andresourceId
functions in your Bicep file. You can access any resource in Bicep by using the symbolic name. By using the symbolic name, you create an implicit dependency between resources - You can still create explicit dependencies by using a
dependsOn
block, but see notes below about this
resource myResource1 'Microsoft.Network/virtualWans@2021-02-01' = {
name: 'resourceName'
location: 'location'
// Even though explicit dependencies are sometimes required, the need for them is rare
// In most cases, you can use a symbolic name to imply the dependency between resources
// If you are setting explicit dependencies, you should consider if there's a way to remove it
dependsOn: [
myResource2
myResource3
]
}
To reference a resource that already exists, use the existing
keyword in a resource declaration
How to define an existing resource in the same targetScope as the current deployment
resource myResource2 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
name: 'examplestorageaccount'
}
Optionally, you can set the scope
property to access an existing resource in a different scope
resource myResource3 'Microsoft.Storage/storageAccounts@2019-06-01' existing = {
name: 'examplestorage'
scope: resourceGroup(otherRG)
}
- Child resources are resources that exist only within the context of another resource
- Each 'parent' resource accepts only certain 'child' resources. Check out the Bicep Resource Reference for the supported parent/child relationships.
- There are different ways you can declare a child resource:
- 'resourceType' for children can be the simple name
shares
, since the full resourceType pathMicrosoft.Storage/storageAccounts/fileServices/shares
is assumed from the parent resource - 'apiVersion' for children is optional, and if omitted the children will use the apiVersion of the parent resource
- 'name' for children can be the simple name
secondChildName
, since the full nameparentName/childName/secondChildName
is assumed from the parent resource - Children can reference Parent resources with symbolic naming. But, Parents can NOT reference Child resources, this would cause a cyclic-dependency
resource parentSymbolicName 'Microsoft.Storage/storageAccounts@2021-04-01' = {
name: 'parentName'
propertiesOfParent
propertiesOfParent
resource childSymbolicName 'fileServices' = {
name: 'childName'
propertiesOfChild
propertiesOfChild
resource secondChildSymbolicName 'shares' = {
name: 'secondChildName'
propertiesOfSecondChild
}
}
}
- The Child resource uses a
parent
parameter that points to the symbolic name of the parent resource - Benefit over Method 1: allows you to define the Child resources in a different Bicep file than the Parent resources
- Benefit over Method 1: allows you to use a loop on Child resources
- The 'resourceType' for children must be the full resourceType path
Microsoft.Storage/storageAccounts/fileServices/shares
- The 'apiVersion' for children must be provided
- The 'name' for children can be just the simple name
childName
, since the full nameparentName/childName
is assumed from the parent resource
resource childSymbolicName 'Microsoft.Storage/storageAccounts/fileServices@2021-04-01' = {
parent: parentSymbolicName
name: 'childName'
propertiesOfChild
propertiesOfChild
}
Method 3. The Child resources are defined separately in their own top-level resource (same as above)
- At first glance, this looks the same as Method 2. But, with Method 3 the Child resource does NOT use a
parent
parameter - The 'resourceType' for children must be the full resourceType path
Microsoft.Storage/storageAccounts/fileServices
- The 'apiVersion' for children must be provided
- The 'name' for children must be the full name
parentName/childName
- Dependencies are NOT inferred with this method, so you must include a
dependsOn
block and manually set dependencies as needed - This method is NOT recommended
resource secondChildSymbolicName 'Microsoft.Storage/storageAccounts/fileServices@2021-04-01' = {
name: 'parentName/childName'
propertiesOfChild
propertiesOfChild
dependsOn: [
parentSymbolicName
]
}
- An extension resource is meant to modify another resource
- A full list of extension resources can be found here
Example 1: By default, an extension resource will target what you have in your targetScope
parameter
resource myExtensionResource1 'Microsoft.Authorization/locks@2016-09-01' = {
name: 'lockName'
properties: {
level: 'CanNotDelete'
}
}
Example 2: This example sets the targetScope
to subscription
, so this extension resource will target a Subscription
targetScope = 'subscription'
resource myExtensionResource2 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = {
name: 'roleAssignmentName'
properties: {
principalId: 'idOfPrincipal'
roleDefinitionId: 'idOfRole'
}
}
Example 3: Deploy an Extension Resource to a specific resource using the scope
parameter
- This example creates a Role Assignment on the resource which has the symbolic name 'myResource3'
resource myExtensionResource3 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = {
name: 'roleAssignmentName'
scope: myResource3
properties: {
principalId: 'idOfPrincipal'
roleDefinitionId: 'idOfRole'
}
}
- A module is just a Bicep file that is deployed from another Bicep file, allowing you to reuse code
- The module (Bicep file) can be a local file or stored in a Registry
- The
name
property is required. It becomes the name of the nested deployment resource in the generated ARM template
module myModule1 '../someFile1.bicep' = {
name: 'myModule1Deployment'
params: {
myModule1Param1: 'something'
myModule1Param2: 'something'
myModule1Param3: 'something'
}
}
How to deploy a Module to a different scope using the scope
parameter
Note
This is how you can deploy resources to a scope that is different than your 'targetScope' parameter
module myModule2 '../someFile2.bicep' = {
name: 'myModule2Deployment'
scope: 'anotherSubscription'
params: {
myModule2Param1: 'something'
myModule2Param2: 'something'
myModule2Param3: 'something'
}
}
How to use a Module (Bicep file) in a Registry
br:
is the schema name for a Bicep Registry- Optionally, you can configure Bicep Registry 'aliases' in your
bicepconfig.json
file and use the alias instead of the full registry path. See this for more info- Starting with Bicep v0.5.6 there is a default alias called
public
which points at Microsoft's official registry of Bicep modules. - An example would be:
br/public:network/virtual-network:1.0
- Here is a link to the official GitHub repo containing the current listing of modules. Microsoft admits the registry is fairly barebones right now, as they are just getting started at the time of this writing.
- Starting with Bicep v0.5.6 there is a default alias called
module myModule3 'br:exampleregistry.azurecr.io/bicep/modules/storage:v1' = {
- Use Outputs when you need to return certain values from a deployment
- Make sure you don't create outputs for sensitive data. Output values can be accessed by anyone who can view the deployment history. They're NOT appropriate for handling secrets
- Instead of passing property values around through outputs, use the
existing
keyword to look up properties of resources that already exist. It's a best practice to look up keys from other resources in this way instead of passing them around through outputs. You'll always get the most up-to-date data - Outputs must set a specific data type
output myOutput1 int = myResource4.properties.maxNumberOfRecordSets
If the property being returned has a hyphen in the name, you can NOT use the dot notation as shown above. Instead, you must use brackets around the property
output myOutput2 string = myResource1['some-property']
You can programmatically grab outputs from successful deployments
PowerShell:
(Get-AzResourceGroupDeployment -ResourceGroupName <rgName> -Name <deploymentName>).Outputs.myOutput1.value
Azure CLI:
az deployment group show -g <rgName> -n <deploymentName> --query properties.outputs.myOutput2.value
- Parameter Files
- Conditions (If)
- Loops
- Miscellaneous
Instead of storing parameter values directly in your .bicep
file, you can store the values externally in a .bicepparam
or .json
file. Then, you'd pass this parameter file, along with the .bicep
file, to your deployment.
Note
This guide is only going to cover the newer .bicepparam
files. If you'd like to know more about the older .json
parameter files, then please reference the documentation.
Format of a .bicepparam file
// the using statement
using 'something'
// optional variables
var varName1 = value1
var varName2 = value2
// parameter values
param parName1 = value1
param parName2 = readEnvironmentVariable('someEnvVar')
param parName3 = toLower(value3)
param parName4 = az.getSecret('subscriptionId', 'resourceGroupName', 'keyvaultName', 'secretName', 'secretVersion')
Each .bicepparam
file is tied to a particular .bicep
file. This relationship is defined by the using
statement. There are multiple options for defining the using
statement, see below for more.
- Support for ARM Templates, Bicep Registries, and Template Specs was added in Bicep v0.22.6
// a regular bicep file or arm template
using 'path/to/file.bicep'
using 'path/to/file.json'
// a module from the public bicep registry
using 'br/public:path:tag'
// a module from a private bicep registry
using 'br:registryName.azurecr.io/bicep/path:tag'
using 'br/aliasName:path:tag'
// a template spec
using 'ts:subscriptionId/resourceGroupName/specName:tag'
using 'ts/aliasName:specName:tag'
// NOT tied to anything
using none
Starting with Bicep v0.21.1 you can define optional Variables in your .bicepparam
files.
You can use expressions in the value of each parameter.
- Use the
readEnvironmentVariable
function to pull a value from an environment variable. - Don't store sensitive values in your parameter files. Instead, use the
az.getSecret
function to pull the value from Key Vault.- The
az.getSecret
function can only pull secrets from Key Vault. - The
az.getSecret
function can only be used in aparam
value - The
az.getSecret
function only supports params that have the@secure()
decorator - By default, it will pull the latest version of the secret, unless you specify the
secretVersion
parameter
- The
You can deploy a resource only if a certain condition is met, otherwise the resource will not be deployed
resource myResource4 'Microsoft.Network/dnszones@2018-05-01' = if (condition) {
name: 'myZone'
location: 'global'
}
Note 1:
- ARM evaluates the expressions used inside resource properties before it evaluates the conditional on the resource itself
- Example:
- ResourceB has properties which are referencing the symbolicName of ResourceA
- ResourceA has a condition where it will not be deployed
- ResourceB's references to ResourceA are now invalid, and the deployment will fail with a 'ResourceNotFound' error
- This will fail even if ResourceB has the same condition applied to it as ResourceA
- Use the ternary operator on the properties of ResourceB as a workaround
Note 2:
- You can't define two resources with the same name in the same Bicep file and then use a condition to only deploy one of them
- The deployment will fail, because Resource Manager views this as a conflict
- If you have several resources, all with the same condition for deployment, consider using Bicep Modules. You can create a Module that deploys all the resources, and then put a condition on the module declaration in your main Bicep file.
- To deploy more than one instance of an item, add the
for
expression - Loops are supported on: Variables, Resources, Modules, Properties, Outputs
- Optionally, you can use the
@batchSize()
decorator to specify how many can be created at one time
Example 1: Integer Index
@batchSize(int)
resource myResource5 'Microsoft.Storage/storageAccounts@2021-08-01' = [for i in range(int1,int2): {
name: 'something-${i}'
}]
Example 2: Array elements
@batchSize(int)
resource myResource6 'Microsoft.Storage/storageAccounts@2021-08-01' = [for item in array: {
name: item.property1
}]
Example 3: Array and Index
@batchSize(int)
resource myResource7 'Microsoft.Storage/storageAccounts@2021-08-01' = [for (item, i) in array: {
name: 'something-${i}'
location: item.property1
}]
Example 4: Complex Object / Dictionary
@batchSize(int)
resource myResource8 'Microsoft.Storage/storageAccounts@2021-08-01' = [for item in items(object): {
name: item.value.property1
}]
How to create a Resource with a loop and a condition
@batchSize(int)
resource myResource9 'Microsoft.Storage/storageAccounts@2021-08-01' = [for item in array: if(item.property1 == true) {
name: item.property2
}]
// This is a single-line comment
/*
This is a
multi-line comment
*/
- All strings in Bicep support interpolation
- To inject an expression surround it by
${
and}
var lastName = 'Anderson'
var firstName = 'Thomas'
var fullName = '${lastName}, ${firstName}'
The ternary operator is a way to embed an if/then/else statement inside the properties of a resource.
condition ? valueIfTrue : valueIfFalse
The true or false values can be of any data type: string, integer, boolean, object, array
Bicep has a large assortment of functions that can be used in your template. Check out the officials docs for more information about all of the available Functions and their instructions.
- Support for User-defined Functions requires Bicep v0.26.54 or later
- Allows you to create and use your own custom functions within a Bicep file
- Great for when you have complicated expressions that are used repeatedly in your Bicep files
- They can be nested, you can call a User-defined Function from another User-defined function
- They support custom User-defined Data Types
- Some limitations:
- Can't access variables
- Can only use parameters that are defined in the function
- Parameters defined in the function can't have default values
- Can't use the
reference
orlist
functions
- Read the docs for more information on User-defined Functions
// create a user-defined function
func functionName (argName dataType, argName dataType, ...) functionDataType => expression
// first, define the functionName that you want to give to this user-defined function
// then, define any arguments that the function uses, specify a name and a data type for each argument
// then, define the data type of the function's return value
// finally, define what value the function will return by creating a custom expression that uses the arguments
- Lambda Expressions can only be used as arguments on the following specific functions:
filter()
,map()
,reduce()
,sort()
- Supported with Bicep v0.10.61 onwardtoObject()
- Supported with Bicep v0.14.6 onwardgroupBy()
,mapValues()
- Supported with Bicep v0.27.1 onward
- The general format of a Lambda Expression is
lambdaVariable => lambdaExpression
. - Read the docs for more information and examples for Lamba Expressions.
Example: filter()
filter(inputArray, lambdaExpression)
// input: array
// lambaExpression: expression/test to run against each element of the array
// - Supports an optional index as of Bicep v0.27.1
// output: array (containing only elements that passed the test)
// example
var inputArray = [
{
name: 'Reba'
age: 24
}
{
name: 'Garth'
age: 10
}
]
output someOutput array = filter(inputArray, item => item.age > 15)
// This returns only elements of the array that have an age value greater than 15,
someOutput = [
{
name: 'Reba'
age: 24
}
]
Example: groupBy()
groupBy(inputArray, lambdaExpression)
// input: array
// lambdaExpression: the expression used to group the array elements
// output: object (containing the grouped elements)
//example
var inputArray = ['Reba', 'Rodney', 'Garth', 'Gary']
output someOutput object = groupBy(inputArray, item => substring(item, 0, 1))
// This returns an object which groups the array elements by their first character
someOutput = {
R: ['Reba', 'Rodney']
G: ['Garth', 'Gary']
}
Example: map()
map(inputArray, lambdaExpression)
// input: array
// lambaExpression: whatever manipulation you want
// - Supports an optional index as of Bicep v0.27.1
// output: array (containing your manipulated elements)
// example
var inputArray = [
{
name: 'Reba'
age: 24
}
{
name: 'Garth'
age: 10
}
]
output someOutput1 array = map(inputArray, item => item.name)
// This returns an array containing just the values of each name:
someOutput1 = [
'Reba'
'Garth'
]
output someOutput2 array = map(inputArray, item => 'Hello ${item.name}!')
// This returns an array which concatenates text to each name:
someOutput2 = [
'Hello Reba!'
'Hello Garth!'
]
Example: mapValues()
mapValues(inputObject, lambdaExpression)
// input: object
// lambdaExpression: the expression used to modify each value
// output: object (containing modified values)
// example
var inputObject = {
something: 'foo'
anotherthing: 'bar'
}
output someOutput object = mapValues(inputObject, item => toUpper(item))
// This returns the same object, but with modified values. In this case, the values are converted to uppercase:
someOutput = {
something: 'FOO'
anotherthing: 'BAR'
}
Example: reduce()
reduce(inputArray, initialValue, lambdaExpression)
// input: array
// initialValue: the initial starting value for comparison
// lambdaExpression: expression used to aggregate the current value with the next value
// - Supports an optional index as of Bicep v0.27.1
// output: any
var inputArray = [5, 3, 2, 8]
output someOutput int = reduce(inputArray, 0, (currentItem, nextItem) => currentItem + nextItem)
// This returns an integer which is the result of adding each item in the array to the next item, starting with 0
// For example: ((((0 + 5) + 3) + 2) + 8)
someOutput = 18
Example: sort()
sort(inputArray, lambdaExpression)
// input: array
// lambdaExpression: an expression that compares one array element to another
// output: array (elements are sorted per your expression)
// example
var inputArray = [
{
name: 'Reba'
age: 24
}
{
name: 'Garth'
age: 10
}
{
name: 'Toby'
age: 2
}
]
output someOutput array = sort(inputArray, (item1, item2) => item1.age < item2.age)
// This returns an array with the exact same elements, however they are sorted by age, lowest to highest
someOutput = [
{
name: 'Toby'
age: 2
}
{
name: 'Garth'
age: 10
}
{
name: 'Reba'
age: 24
}
]
Example: toObject()
toObject(inputArray, lambdaExpression, [lambdaExpression])
// input: array
// lambdaExpression: defines the key of each element for the output object
// optional lambdaExpression: defines the value of each element for the output object
// output: object
// example
var inputArray = [
{
name: 'Reba'
age: 24
}
{
name: 'Garth'
age: 10
}
]
output someOutput1 object = toObject(inputArray, item => item.name)
// This creates a dictionary object, where the key of each element is the 'name'
// Since the optional 2nd lambdaExpression was omitted, then the value becomes the original array element
someOutput1 = {
Reba: {
name: 'Reba'
age: 24
}
Garth: {
name: 'Garth'
age: 10
}
}
output someOutput2 object = toObject(inputArray, item => item.name, item => item.age)
// This creates a dictionary object, where the key of each element is the 'name' and the value of each one is 'age'
// This example includes the optional 2nd lambaExpression, and we get a return value like this:
someOutput2 = {
Reba: 24
Garth: 10
}
- First, you can specify the
@export()
decorator on any User-defined Type (type
), User-defined Function (func
), or Variable (var
). This marks the item as being exportable. - Then, you can use the
import
function, in a totally different Bicep file, to import thattype
,func
, orvar
from the first Bicep file. - Support for Compile-time Imports is generally available as of Bicep v0.25.3
Example exports.bicep
file:
@export()
type myUserDefinedType = {
something: string
anotherthing: int
}
@export()
func myUserDefinedFunction(name string) string => 'Hey there ${name}'
@export()
var myVariable = 'some constant value'
// can only use constant values, or references to other variables
// can not use references to resources, modules, or parameters
Example main.bicep
file:
// method 1, import the given items from exports.bicep
import {myUserDefinedType, myVariable} from 'exports.bicep'
// method 1, same as above, but define an optional alias that you can use
import {myVariable as newVariableAlias} from 'exports.bicep'
// method 2, import everything from exports.bicep into the symbolic name allImports
// then reference each item like so: allImports.myUserDefinedType, allImports.myUserDefinedFunction, allImports.myVariable
import * as allImports from 'exports.bicep'
Support for .bicepparam
files
- As of Bicep v0.22.6, you can import Variables in your
.bicepparam
files - As of Bicep v0.26.54, you can import User-defined Functions in your
.bicepparam
files
PowerShell:
((Get-AzResourceProvider -ProviderNamespace Microsoft.Batch).ResourceTypes | Where-Object ResourceTypeName -eq batchAccounts).Locations
Azure CLI:
az provider show --namespace Microsoft.Batch --query "resourceTypes[?resourceType=='batchAccounts'].locations | [0]" --out table
I've written a whole series of articles describing the different methods that can be used to deploy Bicep:
I've also included some example files in this repo: