Bicep Management Group Templates

Table of Contents

Management Group Hierarchy

Deploying Azure Management Groups hierarchy to your Tenant is a good start to implement CAF (Cloud Adoption Framework) and in this post I’ll show two different ways to deploy this with Bicep templates.

Expected outcome of Management Group Hierarchy template deployment

Common deployment settings

With the templates I’m using a json file to describe the Management Group hierarchy as an object.
First there are 4 common settings that need to be specified:

1
2
3
4
5
6
{
    "managementGroupPrefix": "MYC",
    "managementGroupSuffix": "mg",
    "defaultManagementGroup": "newsubscriptions",
    "requireAuthorizationForGroupCreation": true,
}
  • managementGroupPrefix is the common prefix
  • managementGroupSuffix is the common suffix
  • defaultManagementGroup is the ID of the management group you want to be the default container for newly created/imported subscription.
  • requireAuthorizationForGroupCreation if set to true, then users needs specific permissions to create new management groups. The default setting in Azure is that any user can create management groups.

With the above settings

  • the platform management group Id would be myc-xxx-mg
  • new or imported subscriptions will be contained in myc-newsubscriptions-mg
  • users need permissions to create new management groups.

Further down in the setting file the actual representation of the management groups differ depending on the template used…


‘Parallel’ deployment

This template is hard-coded with 6 tiers for the management groups as that is the limit set by Azure.
And we need to specify the explicit dependsOn dependency as the child management groups can’t be created before their parents.

settings file

Also, the settings file reflects each tier and the hierarchy should be pretty obvious.

  • name is used together with the prefix and suffix to build the name of the management group
  • displayName is prefixed and then used as the name of the management group
  • parent is set to the name of the management group that is it’s parent

bicep template

deploy

With AZ CLI

1
2
3
4
5
6
# Log in
az login 
# Set subscription
az account set --subscription 12345678-AAAA-BBBB-CCCC-123456789012   
# Deploy
az deployment tenant create --location swedencentral --template-file ./managementgroups.bicep --parameters managementGroupObject=@default-managementgroups.json

With Powershell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Log in
Connect-AzAccount
# Set subscription
Set-AzContext -SubscriptionId 12345678-AAAA-BBBB-CCCC-123456789012
# Deploy
$Props = @{
    Name = ("MGMT_{0}" -f (get-date -f s).replace(':', ''))
    Location = 'swedencentral'
    TemplateFile = './managementgroups.bicep'
    TemplateParameterObject = @{
          managementGroupObject = Get-Content -Path './default-managementgroups.json' | ConvertFrom-Json -AsHashTable
        }
}

$Deployment = New-AzTenantDeployment @Props
$Deployment

This will deploy all the management groups in serial from first tier to the last, but in parallel in each tier.


‘Serial’ deployment

Another way to do this is to deploy all of the management groups i serial, increasing the overall time to deploy.

settings file (serial)

This file is a bit different as you need to set the tier for the management group in each object

  • name is used together with the prefix and suffix to build the name of the management group
  • displayName is prefixed and then used as the name of the management group
  • parent is set to the name of the management group that is it’s parent
  • tier set this to the tier the management group should be created in, (integer)

bicep template (serial)

As shown, the template is much smaller and is using the new lambda function in bicep to sort all the management groups in correct order starting from the root.
And then using the batchSize set to 1 to deploy all management groups in order.

deploy (serial)

With AZ CLI

1
2
3
4
5
6
# Log in
az login 
# Set subscription
az account set --subscription 12345678-AAAA-BBBB-CCCC-123456789012   
# Deploy
az deployment tenant create --location swedencentral --template-file ./managementgroups-serial.bicep --parameters managementGroupObject=@default-managementgroups-serial.json

With Powershell

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Log in
Connect-AzAccount
# Set subscription
Set-AzContext -SubscriptionId 12345678-AAAA-BBBB-CCCC-123456789012
# Deploy
$Props = @{
    Name = ("MGMT_{0}" -f (get-date -f s).replace(':', ''))
    Location = 'swedencentral'
    TemplateFile = './managementgroups-serial.bicep'
    TemplateParameterObject = @{
      managementGroupObject = Get-Content -Path './default-managementgroups-serial.json' | ConvertFrom-Json -AsHashTable
    }
}

$Deployment = New-AzTenantDeployment @Props
$Deployment

Conclusion

The outcome of both templates are the same, it’s a matter of time and preference. And if you would chain these templates in a pipeline, adding outputs to the template would be recommended but is not included in this example.


Related Posts