DEV Community

Olivier Miossec
Olivier Miossec

Posted on • Edited on

Multi Scopes Deployment with Azure Bicep

As cloud professionals, one of your tasks is to deliver Subscriptions (or accounts on AWS) to development teams. But it is never a naked empty subscription, it is a pre-configured subscription with all the configuration and connectivity so teams can focus on what they do best, deploying applications.
In Azure, configuration can be made on several scopes. In the management group scope, you need to move the subscription inside a specific management group, subscription level, you will need to create resource groups and manage RBAC, and finally, at the resource group level deploy resources like VNET, VNET peering, and other essential tooling.
One of the problems is that you need to manage 3 different pipelines. One for each scope. It is time-consuming; they have their own lifetime and they can become error-prone with time.
In Bicep, by default, it is not possible to target multiple scopes in one template. You need to create several templates. Unless you use Bicep modules.
A Bicep module is a Bicep template that is deployed from another Bicep. You can call it from the local file system or from a private registry. Modules allow you to simplify your deployments by reusing things you often deploy. But, there is a very interesting feature with module, you can specify the scope of the deployments when you use a module. In other words, you can create a multi-scope template.
Let’s try a simple example; you have a new subscription, you need to create a resource group, and you want to create a VNET inside this resource group and then create a peering to another VNET in another subscription.
Here we have 2 scopes, the management group scope, the subscription scope, and the resource group scope. The last one needs to address different subscriptions.
We need to start at the higher level of the hierarchy, here it is the subscription scope.
You need to use the targetScope keyword. It can have 4 values; tenant, managementGroup, subscription, and resourceGroup. Here we need to use managementGroup
We need to deploy objects and resources to different scopes. We need to create modules

  • Vnet.bicep to create the VNET
  • Peering.bicep to create the 2 peering we need

For the VNET module

param virtualNetworkName string
param virtualNetworkAddressPrefix string
param location string 
param subnetName string
param subnetAddressPreffix string

resource virtualNetwork 'Microsoft.Network/virtualNetworks@2020-11-01' = {
  name: virtualNetworkName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        virtualNetworkAddressPrefix
      ]
    }
    subnets: [
      {
        name: subnetName
        properties: {
          addressPrefix: subnetAddressPreffix
        }
      } 
    ]
  }
}
output vnetID string = virtualNetwork.id
Enter fullscreen mode Exit fullscreen mode

Note: I output the VNET ID so I can use it later

And for the peering

param peeringName string
param vnetName string
param localPrefix string
param remotePrefix string  
param remoteVnetID string

var completePeeringName = '${vnetName}/${peeringName}'

resource peering 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2023-04-01' = {

  name: completePeeringName
  properties: {
    allowForwardedTraffic: true

    allowVirtualNetworkAccess: true

    remoteAddressSpace: {
      addressPrefixes: [
        localPrefix
      ]
    }
remoteVirtualNetwork: {
      id: remoteVnetID
    }
    remoteVirtualNetworkAddressSpace: {
      addressPrefixes: [
        remotePrefix
      ]
    }  
  }
}
Enter fullscreen mode Exit fullscreen mode

Note: You need to compose the name of the peering with the name of the VNET vnetName/peeringName

Now we can start to deploy resources. In the main.bicep template.

targetScope = 'subscription'
Enter fullscreen mode Exit fullscreen mode

Then we can start to create the resource group we need.

targetScope = 'subscription'

param subscriptionID string
param azureRegion string = 'westeurope'
param resourceGroupName string 


resource myResourceGroup 'Microsoft.Resources/resourceGroups@2021-01-01' = {
  name: resourceGroupName
  location: azureRegion 
}
Enter fullscreen mode Exit fullscreen mode

Then we call the VNET module to deploy the VNET.

module virtualNetwork './modules/vnet.bicep' = {
  scope: myResourceGroup
  name: 'virtualNetwork'
  params: {
    virtualNetworkName: 'vnet1'
    virtualNetworkAddressPrefix: '10.0.0.0/24'
    location: 'westeurope'
    subnetName: 'default'
    subnetAddressPreffix: '10.0.0.0/24'
  }
}
Enter fullscreen mode Exit fullscreen mode

For the scope, I use the symbolic name of the resource group deployment.
Then we need to deploy our first peering

var hubVnetID = '/subscriptions/xxx/resourceGroups/centralhubvnet/providers/Microsoft.Network/virtualNetworks/hubvnet'

module peering01 './modules/peering.bicep' = {
  scope: myResourceGroup
  name: 'peering01'
  params: {
    peeringName: 'toHub'
    vnetName: 'vnet1'
    localPrefix: '10.1.0.0/24'
    remotePrefix: '172.24.20.0/24'
    remoteVnetID: hubVnetID
  }
}
Enter fullscreen mode Exit fullscreen mode

We can now deploy the second peering. But if we are still on the same scope, resourceGroup scope, we need to deploy it in a different subscription and resource group. The function resourceGroup() lets you do it by using the subscription ID and the resource group name

var hubVnetSubscriptionID   = 'xxxxxxx'
var hubVnetResourceGroup    = '01-centralhubvnet'
var hubVnetName             = 'hubvnet'

module peering02 './modules/peering.bicep' = {
  name: 'peering02'
  scope: resourceGroup(hubVnetSubscriptionID, hubVnetResourceGroup)
  params: {
    peeringName: 'fromHUB'
    vnetName: hubVnetName
    localPrefix: '172.24.20.0/24'
    remotePrefix: '10.1.0.0/24' 
    remoteVnetID: virtualNetwork.outputs.vnetID
  }
}
Enter fullscreen mode Exit fullscreen mode

To deploy the main template simply use this command

New-AzSubscriptionDeployment  -Name demo01 -Location westeurope -TemplateFile .\main.bicep -resourceGroupName demoBicep
Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
mrtaunus profile image
Tuukka Haapaniemi

In the text you state that "Here we need to use managementGroup". From the code samples you provide, however, I can't seem to find any reference to management group, only to a subscription scope. Are the resources you are deploying in the same subscription?

I'm asking, because this is just the problem I have. I need to deploy an App Service to a single subscription, but be able to reference another subscription to get the common existing App Service Plan, and can't seem to make it work using targetScope: subscription. But using tenant or managementGroup, I run into other errors, such as

ERROR: The target scope "tenant" does not match the deployment scope "subscription".