Bicep | Deployment Scope Hopping

Posted by Andrew Wilson on Tuesday, April 25, 2023

Background

An Azure Tenant is hierarchically structured with the following make up:

  • Tenant
  • One or more Management Groups
  • One or more Subscriptions
  • One or more Resource groups
  • One or more Resources

AzureScopes

Deployment Scopes {Tenant, Management Group, Subscription, Resource Group} allow us to deploy respective types of resources at each level.

A Scope is dictated by two attributes, the selected scope level, and the identifier of the item at that scope level. For example, if I am to deploy at the Subscription Scope, I need to both Identify that I am doing a Subscription Deployment, and I need to provide the identifier of the Subscription. Resource Groups are the lowest in the list of Scope Levels.

SingleScopeDeployment

Single Scope Deployment

In a single scoped deployment, we essentially design our Bicep templates to target a single scope level such as Resource group. In this type of deployment we only deploy respective resources to a specific Resource Group and no other. For Example:

SingleScopeDeployment

In Bicep this would appear as follows:

targetScope = 'resourceGroup'

// Parameters
...
// Variables
...
// Resources

resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = {
	...
}

resource storageaccount 'Microsoft.Storage/storageAccounts@2021-02-01' = {
	...
}

// Outputs
...

Parent Child Deployment Scope Hopping

Parent Child Deployment Scope Hopping is where we choose a parent scope that will effectively be our starting point in conducting deployments. In this type of deployment, we can conduct deployments in the parent scope and in each child. For Example:

ParentChildScopeDeployment

In Bicep this would appear as follows:

targetScope = 'subscription'

// Parameters
@description('Name of the Second existing Resource Group')
param resourceGroup2Name string
...
// Variables
...
// Resources

// Deploy Resource Group 1
resource RG1Deploy 'Microsoft.Resources/resourceGroups@2022-09-01' = {
...
}

// Deploy Resources into Resource Group 1
module rg1 'rg1.bicep' = {
	scope: RG1Deploy
	...
}

// Deploy Resources into Existing Resource Group 2
module rg2 'rg1.bicep' = {
	scope: ResourceGroup(resourceGroup2Name) // Use the ResourceGroup function to obtain the reference to the second RG
	...
}

// Outputs
...

If we choose the Management Group Scope for our deployment, we can conduct deployments in the selected Management Group, each linked Subscription, and furthermore each Resource group under each Subscription.

Sibling Scope Hop

Sibling Deployment Scope Hopping is where we choose a scope that will effectively be our starting point in conducting deployments, but from this scope move to deploy into another scope but on the same scope level.

An example would be where we wish to deploy resources into different resource groups. The Subscriptions can be different, all that is required is the deployment principal be provided the relevant rights to the Resource Groups such as Contributor. The benefit of this approach is rights do not need to be supplied to a higher scope of which would be applied to child items.

SiblingScopeHop

In the diagram above, we are looking to use the first Resource Group as the starting scope. Each hop will then point to a different Resource Group but in the same scope level. For the second and third hop, we will also need to provide a Subscription Id as we will not be able to reference by Resource Group name only.

The corresponding Bicep for the diagram above would appear as such:


targetScope = 'resourceGroup'

// Parameters
@description('Name of the Second existing Resource Group')
param resourceGroup2Name string

@description('Subscription Id for the 3rd Resource Group')
param subscription2Id string
@description('Name of the 3rd existing Resource Group')
param resourceGroup3Name string

@description('Subscription Id for the 4th Resource Group')
param subscription3Id string
@description('Name of the 4th existing Resource Group')
param resourceGroup4Name string

...
// Variables
...
// Resources

// Deploy Resources into Resource Group 1
// No Scope applied as we are deploying to the starting scope
module rg1 'rg.bicep' = {
	...
}

// Deploy Resources into Resource Group 2
// Only need to specify ResourceGroup function with a name as still under the same subscription
module rg2 'rg.bicep' = {
	scope: ResourceGroup(resourceGroup2Name)
	...
}

// Deploy Resources into Resource Group 3
// Need to specify ResourceGroup function with a name and subscription
module rg3 'rg.bicep' = {
	scope: ResourceGroup(subscription2Id, resourceGroup3Name)
	...
}

// Deploy Resources into Resource Group 4
// Need to specify ResourceGroup function with a name and subscription
module rg4 'rg.bicep' = {
	scope: ResourceGroup(subscription3Id, resourceGroup4Name)
	...
}

// Outputs

Mix and Match

Usage of these methods can vary and can also be combined. For instance you could combine both a Parent Child Scope hop with Sibling hops. How you structure your deployments will depend on the architecture and resource dependencies at play.

You may find yourself looking to deploy Key Vault in one Resource Group and a Web App in another. Your Web App might require access to the Key Vault instance and therefore an Access Policy will need deploying. Rather than navigating a potential parent child deployment, why not do a sibling hop?