In this post, I am going to write about what an Azure Bicep output is and how you can work with output, and how you can access nested child resources in Azure Bicep.
Using outputs can be useful to retrieve specific resource or module properties (for example resourceIds or names) and pass these to input parameters for resources or modules.
Output definition
Let’s take a look at what an output is, what it can do and how you define one and use it.
What is it?
An output is a value that is used to return values from the deployed resources. When an output is defined, it always has a data type. This data type can be: string, array, bool, int, or an object
What can you do with it?
Output can be used to pass through return values from a resource or module to another resource or model. You can compare it to a return value from a method in programming languages.

In the example above, you see the list of outputs the resRemoveVirtualNetwork
(virtual network) has. These values are default outputs that the virtual network resource has, and these outputs can be used in other modules or resources.
Define an output in Bicep
Azure Bicep provides resources with default properties. In the last example, you saw an example for a virtual network, which has properties like name, id or apiVersion, and so on. These are the outputs for the virtual network resources. When you write your own Bicep modules and, you want that module to have certain outputs, you have to define these yourself.
The definition of output is as follows:
output <name> <data-type> = <value>
Let’s work with an example. You need to create a module to create network interface cards (NIC) for virtual machines. You also add a subnet and a network security group (NSG). In this module, you define the following resources:
- Virtual Network (existing)
- Network Security Group
- Network Interface Card
You want to access the properties from outside the module, so you have to define the output of the network interface resourceId because you want to use this id for your virtual machine. In your module, define the resources and add the outputs:
param parVnetObject object | |
param parNsgObject object | |
param parNicObject object | |
resource resVirtualNetwork 'Microsoft.Network/virtualNetworks@2021-08-01' existing = { | |
name: parVnetObject.name | |
} | |
resource resNetworkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2021-08-01' = { | |
name: parNsgObject.Name | |
properties: { | |
securityRules: parNsgObject.securityRules | |
} | |
} | |
resource resNic 'Microsoft.Network/networkInterfaces@2021-08-01' = { | |
name: parNicObject.name | |
properties:{ | |
ipConfigurations: [ | |
{ | |
name: 'ipconfig1' | |
properties: { | |
subnet: resVirtualNetwork.properties.subnets[0] | |
} | |
} | |
] | |
networkSecurityGroup: { | |
id: resNetworkSecurityGroup.id | |
} | |
} | |
} | |
output outNicId string = resNic.id |
Now when you reference this module in a Bicep file, you can access the defined output. Let’s define the module and reference the exposed output outNicId
to the network interface id for a virtual machine:
module modNetworking 'networking.bicep' = { | |
name: 'deploy-network-components' | |
params: { | |
parNicObject: { | |
name: 'my-nic' | |
} | |
parNsgObject: { | |
name: 'my-nsg' | |
securityRules: [] | |
} | |
parVnetObject: { | |
name: 'my-existing-vnet' | |
} | |
} | |
} | |
resource resMyVm 'Microsoft.Compute/virtualMachines@2021-11-01' = { | |
name: 'my-vm' | |
properties: { | |
networkProfile: { | |
networkInterfaces: [ | |
{ | |
id: modNetworking.outputs.outNicId | |
} | |
] | |
} | |
} | |
} |
Create a module called modNetworking
and create a compute resource called resMyVm
. The virtual machine resource requests a network interface id for the network interface card. By using the function accessor using the .
(dot) you can access the output you defined in the module:

The Bicep extension for Visual Studio Code gives access to autocomplete functions.
Access a nested resource using :: the child accessor operator
To access a nested child resource, there is a different approach. The problem with accessing a child resource is that you can’t reference the symbolic name of the child directly. You have to access the child through the parent. This is where the ::
operator comes to play.
Let’s take a look at the following example:
resource resMyExistingVnet 'Microsoft.Network/virtualNetworks@2021-08-01' = { | |
name: 'my-exiting-virtual-network-name' | |
resource resMyFirstSubnet 'subnets' existing = { | |
name: 'my-existing-first-subnet' | |
} | |
resource resMyOtherSubnet 'subnets' existing = { | |
name: 'my-other-subnet' | |
} | |
} |
In this example, you see an existing virtual network with two dependency subnets. With Azure Bicep, you can define a parent and child construction to create child resources using the parent resource type.
If you want to get the resourceId of the child resMyOtherSubnet
you cannot get this the regular way by accessing the outputs of the parent. To get the resourceId of resMyOtherSubnet
you have to use the operator ::
. The ::
operator allows accessing a child resource within its parent. You have to use this with the symbolic name of the parent and child resource.
A nested resource declaration must appear at the top level of syntax of the parent resource.

resource resMyExistingVnet 'Microsoft.Network/virtualNetworks@2021-08-01' existing = { | |
name: 'my-exiting-virtual-network-name' | |
resource resMyFirstSubnet 'subnets' existing = { | |
name: 'my-existing-first-subnet' | |
} | |
resource resMyOtherSubnet 'subnets' existing = { | |
name: 'my-other-subnet' | |
} | |
} | |
output outFirstSubnet string = resMyExistingVnet::resMyFirstSubnet.id | |
output outOtherSubnet string = resMyExistingVnet::resMyOtherSubnet.id | |
output outVnetId string = resMyExistingVnet.id |
Conclusion
In this blog, you read how you can work with outputs and how you can access outputs of nested child resources with a special operator. Outputs from modules are not limited to resourceIds, this can also be a name of a resource or manipulated values. You can also work with conditional outputs, dynamic outputs and use these outputs in scripts.