Working with different scopes with Azure Bicep Deployments

Working with different scopes with Azure Bicep Deployments

An issue with many getting started templates and tutorials for Azure Bicep is that everything is deployed into one single resource group, and afterwards destroyed with the same resource group. It is very simple for demos and guides to have it this way but once you move into the real world of cloud development this is more often than not the case.

The default scope of deployments with Azure Bicep is at the resource group level. You can set different scopes manually in your Bicep file using targetScope and set it to Subscription or Management Group for instance.

One common question I receive and which is a typical challenge but a very common scenario in the real world is:

  • What happens when I want to deploy to multiple different scopes?
  • What happens when I want to deploy to one resource group but I have dependencies in different resource groups?

Example deployment

Here we can make use of modules in Bicep to split our configuration files up

In this deployment I am deploying to a resource group called rg-prod-we-ddc by running the following command:

az deployment group create \ --template-file main.bicep \ --parameters prod.bicepparam \ --resource-group rg-prod-we-ddc

I have a dependecy to a keyvault inside of another resource group which is not in rg-prod-we-ddc

param environment string
param location_short string
param core_name string

param vm_size string
param vm_image_reference_settings object
param vm_os_disk_settings object
param admin_username string

resource kv_core 'Microsoft.KeyVault/vaults@2023-07-01' existing = {
  name: 'kv-${environment}-${location_short}-${core_name}-0912'
  scope: resourceGroup('rg-${environment}-${location_short}-${core_name}')
}


module network 'modules/network.bicep' = {
  name: 'network'
  params: {
    environment: environment
    location_short: location_short
    core_name: core_name
  }
}

module keyvault 'modules/kv.bicep' = {
  name: 'keyvault'
  scope: resourceGroup('rg-${environment}-${location_short}-${core_name}')
  params: {
    core_name: core_name
    environment: environment
    location_short: location_short
  }
}

module vm 'modules/vm.bicep' = {
  name: 'vm'
  params: {
    environment: environment
    location_short: location_short
    vm_size: vm_size
    vm_image_reference_settings: vm_image_reference_settings
    vm_os_disk_settings: vm_os_disk_settings
    dc_nic_resource_id: network.outputs.dc_nic_resource_id
    admin_username: admin_username
    admin_password: kv_core.getSecret('vm-${environment}-${location_short}-dc01')
  }

  dependsOn: [
    keyvault
  ]
}

On the resource block using the existing keyword (The very first one) we can see that I have set a scope of resourceGroup() and inside it I have entered the name of the RG where my keyvault is deployed. It is using parameters but will be translated into rg-prod-we-core

Take note that this is not the rg-prod-we-ddc resource group that I am targeting with my deploy command.

The purpose of this

It is very common that we will face scenarios where we do not deploy all the resources inside of a subscription into one big resource group, that would be extremely messy.

In my case I have a key vault where I store my VM password temporarily when I deploy a new virtual machine but I also use that key vault for other things. That means the key vault and its secrets does not live and die with this VM resource only.

Another scenario can be that I am also deploying a NIC that will be attached to this virtual machine. In order to create the NIC I need a reference to the VNET and Subnet where it will be registered. I keep my networking configuration inside of a different resource group compared to my compute virtual machine resource in this case.

Hopefully you can see my point here and why understanding and deploying to different scopes is required when working with IaC like this.

Brief note on modules with Azure Bicep

Modules with Bicep allows you to split your configuration up into different files which can be called from your main.bicep Which is what I have done here.

Essentially "A bicep file deployed from another bicep file".

What is beneficial with that is that you can split larger deployments into smaller more manageable chunks of code. You can also set different scopes on different modules in your main.bicep file. I will have a post later on showing how to build them (Or just look at my example code above :) )

About me

About me
If you have landed on my page you will have already understood my passion for tech, but obviously there is more to life than that. Here I will try and outline a few of my other hobbies. Strength training I am a person who loves to move around and challenge