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
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
]
}
}
}
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'
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
}
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'
}
}
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
}
}
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
}
}
To deploy the main template simply use this command
New-AzSubscriptionDeployment -Name demo01 -Location westeurope -TemplateFile .\main.bicep -resourceGroupName demoBicep
Top comments (1)
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".