Azure Azure Governance Azure PowerShell

Azure PowerShell script: Create a management group tree hierarchy

[August 1, 2020 – Original post]


Management groups can be used to effectively manage a company’s various Azure subscriptions and provide them a way to not only control and manage access, but also to apply policies and compliance in an organizational, environment-based or combined hierarchy.

To automate the deployment process of all my planned different child an parent management groups into a tree structure for my Azure test environment, I wrote the below Azure PowerShell script. This script not only allows me to quickly deploy the complete management group hierarchy based on the Enterprise-Scale with hub and spoke architecture. But it is easy to adjust, so it can be used during the deployment of a management group hierarchy for any customer or your own company environment.

This Azure PowerShell script will do all of the following:

  • Create new GUIDs for all management group IDs (GroupName*) and store them in variables.
  • Define all management group display names (DisplayName**) and store them in variables.
  • Check if the PowerShell window is running as Administrator (when not running from Cloud Shell), otherwise the Azure PowerShell script will be exited.
  • Suppress breaking change warning messages.
  • Create a Company management group underneath the tenant root group (root management group).
  • Create top management groups (Platform, Landing Zones, Sandboxes, Decommissioned) underneath the customer management group.
  • Create Platform management groups (Management, Connectivity, Identity) underneath the Platform management group.
  • Create Landing Zones management groups (Corp, Online, SAP) underneath the Landing Zones management group.
  • Move subscriptions which are under the tenant root group at the moment of running the script (see below screenshot as example), to the correct management groups, if they exist.



* The GroupName is a unique identifier which is used by other commands to reference the management group. This ID can not be changed after creation.

** The DisplayName is a human readable name used in the Azure Portal, and which can be changed at all times.


To use the script copy and save it as Create-ManagementGroups-Tree-Hierarchy.ps1 or download it from GitHub. Then before using the script, adjust all variables to your use and then run the customized script with Administrator privileges from Windows TerminalVisual Studio Code, or Windows PowerShell. Or you can simply run it from Cloud Shell.


Prerequisites

  • An Azure subscription.
  • An Azure Administrator account with the necessary RBAC roles.
  • Change all the variables in the script where needed to fit your needs (you can find an adjusted example in one of the screenshots below). For example change the variables <companyFullName> and <companyShortName> to fit your needs.
  • Before building your Management Group hierarchy, make sure to initialize your root hierarchy.




Azure PowerShell script

If you are not running the script from Cloud Shell, don’t forget to sign in with the Connect-AzAccount cmdlet to connect your Azure account. And if you are using multiple Azure subscriptions, select the proper subscription with the Get-AzSubscription cmdlet before running the script.

<#
.SYNOPSIS

A script used to create a management groups tree structure

.DESCRIPTION

A script used to create a management groups tree structure. 
When all management groups are created the Azure subscriptions will be moved to the corresponding management group.

.NOTES

Filename:       Create-Azure-Management-Groups-Tree-Hierarchy.ps1
Created:        31/07/2020
Last modified:  21/04/2022
Author:         Wim Matthyssen
PowerShell:     Azure PowerShell or Azure Cloud Shell
Version:        Install latest Azure PowerShell modules
Action:         Change variables were needed to fit your needs. 
Disclaimer:     This script is provided "As Is" with no warranties.


.EXAMPLE

.\Create-Azure-Management-Groups-Tree-Hierarchy.ps1

.LINK

https://wmatthyssen.com/2022/04/04/azure-powershell-script-create-a-management-group-tree-hierarchy/
#>

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Variables

$companyFullName = "<companyFullName>" # <your company full name here> Example: "myhcjourney"
$companyShortName ="<companyShortName>" # <your company short name here> Best is to use a three letter abbreviation. Example: "myh"

$companyManagementGroupName = "mg-" + $companyFullName 
$companyManagementGroupGuid = New-Guid

$platformManagementGroupName = "mg-" + $companyShortName + "-platform"
$platformManagementGroupGuid = New-Guid
$landingZonesManagementGroupName = "mg-" + $companyShortName + "-landingzones"
$landingZonesManagementGroupGuid = New-Guid
$sandboxesManagementGroupName = "mg-" + $companyShortName + "-sandboxes"
$sandboxesManagementGroupGuid = New-Guid
$decommissionedManagementGroupName = "mg-" + $companyShortName + "-decommissioned"
$decommissionedManagementGroupGuid = New-Guid

$managementManagementGroupName = "mg-" + $companyShortName + "-management"
$managementManagementGroupGuid = New-Guid
$connectivityManagementGroupName = "mg-" + $companyShortName + "-connectivity"
$connectivityManagementGroupGuid = New-Guid
$identityManagementGroupName = "mg-" + $companyShortName + "-identity"
$identityManagementGroupGuid = New-Guid

$corpManagementGroupName = "mg-" + $companyShortName + "-corp"
$corpManagementGroupGuid = New-Guid
$onlineManagementGroupName = "mg-" + $companyShortName + "-online"
$onlineManagementGroupGuid = New-Guid
$sapManagementGroupName = "mg-" + $companyShortName + "-sap"
$sapManagementGroupGuid = New-Guid

$subNameManagement = Get-AzSubscription | Where-Object {$_.Name -like "*management*"}
$subNameConnectivity = Get-AzSubscription | Where-Object {$_.Name -like "*connectivity*"}
$subNameIdentity = Get-AzSubscription | Where-Object {$_.Name -like "*identity*"}
$subNameCorpPrd = Get-AzSubscription | Where-Object {$_.Name -like "*prd*corp*"}
$subNameCorpDev = Get-AzSubscription | Where-Object {$_.Name -like "*dev*corp*"}
$subNameOnlinePrd = Get-AzSubscription | Where-Object {$_.Name -like "*prd*online*"}
$subNameOnlineDev = Get-AzSubscription | Where-Object {$_.Name -like "*dev*online*"}
$subNameSapPrd = Get-AzSubscription | Where-Object {$_.Name -like "*prd*sap*"}
$subNameSapDev = Get-AzSubscription | Where-Object {$_.Name -like "*dev*sap*"}
$subNameTest = Get-AzSubscription | Where-Object {$_.Name -like "*tst*"}

$global:currenttime= Set-PSBreakpoint -Variable currenttime -Mode Read -Action {$global:currenttime= Get-Date -UFormat "%A %m/%d/%Y %R"}
$foregroundColor1 = "Red"
$foregroundColor2 = "Yellow"
$writeEmptyLine = "`n"
$writeSeperatorSpaces = " - "

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Check if PowerShell runs as Administrator (when not running from Cloud Shell), otherwise exit the script

if ($PSVersionTable.Platform -eq "Unix") {
    Write-Host ($writeEmptyLine + "# Running in Cloud Shell" + $writeSeperatorSpaces + $currentTime)`
    -foregroundcolor $foregroundColor1 $writeEmptyLine
    
    ## Start script execution    
    Write-Host ($writeEmptyLine + "# Script started. Without any errors, it will need around 5 minute to complete" + $writeSeperatorSpaces + $currentTime)`
    -foregroundcolor $foregroundColor1 $writeEmptyLine 
} else {
    $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())
    $isAdministrator = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)

        ## Check if running as Administrator, otherwise exit the script
        if ($isAdministrator -eq $false) {
        Write-Host ($writeEmptyLine + "# Please run PowerShell as Administrator" + $writeSeperatorSpaces + $currentTime)`
        -foregroundcolor $foregroundColor1 $writeEmptyLine
        Start-Sleep -s 3
        exit
        }
        else {

        ## If running as Administrator, start script execution    
        Write-Host ($writeEmptyLine + "# Script started. Without any errors, it will need around 5 minutes to complete" + $writeSeperatorSpaces + $currentTime)`
        -foregroundcolor $foregroundColor1 $writeEmptyLine 
        }
}

## ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Suppress breaking change warning messages

Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true"

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Create Company management group

New-AzManagementGroup -GroupName $companyManagementGroupGuid -DisplayName $companyManagementGroupName | Out-Null

# Store Company management group in a variable
$companyParentGroup = Get-AzManagementGroup -GroupName $companyManagementGroupGuid

Write-Host ($writeEmptyLine + "# Company management group $companyManagementGroupName created" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Create Top management groups

# Create Platform management group
New-AzManagementGroup -GroupName $platformManagementGroupGuid -DisplayName $platformManagementGroupName -ParentObject $companyParentGroup | Out-Null

# Create Landing Zones management group
New-AzManagementGroup -GroupName $landingZonesManagementGroupGuid -DisplayName $landingZonesManagementGroupName -ParentObject $companyParentGroup | Out-Null

# Create Sandbox management group
New-AzManagementGroup -GroupName $sandboxesManagementGroupGuid -DisplayName $sandboxesManagementGroupName -ParentObject $companyParentGroup | Out-Null

# Create Decomission management group
New-AzManagementGroup -GroupName $decommissionedManagementGroupGuid -DisplayName $decommissionedManagementGroupName -ParentObject $companyParentGroup | Out-Null

# Store specific Top management groups in variables
$platformParentGroup = Get-AzManagementGroup -GroupName $platformManagementGroupGuid 
$landingZonesParentGroup = Get-AzManagementGroup -GroupName $landingZonesManagementGroupGuid

Write-Host ($writeEmptyLine + "# Top management groups $platformManagementGroupName, $landingZonesManagementGroupName, $sandboxesManagementGroupName, and `
$decommissionedManagementGroupName created" + $writeSeperatorSpaces + $currentTime) -foregroundcolor $foregroundColor2 $writeEmptyLine

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Create Platform management groups

# Create Management management group
New-AzManagementGroup -GroupName $managementManagementGroupGuid -DisplayName $managementManagementGroupName -ParentObject $platformParentGroup | Out-Null

# Create Connectivity management group
New-AzManagementGroup -GroupName $connectivityManagementGroupGuid -DisplayName $connectivityManagementGroupName -ParentObject $platformParentGroup | Out-Null

# Create Identity management group
New-AzManagementGroup -GroupName $identityManagementGroupGuid -DisplayName $identityManagementGroupName -ParentObject $platformParentGroup | Out-Null

Write-Host ($writeEmptyLine + "# Platform management groups $managementManagementGroupName, $connectivityManagementGroupName and `
$identityManagementGroupName created" + $writeSeperatorSpaces + $currentTime) -foregroundcolor $foregroundColor2 $writeEmptyLine

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Create Landing Zones management groups

# Create Corp management group
New-AzManagementGroup -GroupName $corpManagementGroupGuid -DisplayName $corpManagementGroupName -ParentObject $landingZonesParentGroup | Out-Null

# Create Online management group
New-AzManagementGroup -GroupName $onlineManagementGroupGuid -DisplayName $onlineManagementGroupName -ParentObject $landingZonesParentGroup | Out-Null

# Create SAP management group
New-AzManagementGroup -GroupName $sapManagementGroupGuid -DisplayName $sapManagementGroupName -ParentObject $landingZonesParentGroup | Out-Null

Write-Host ($writeEmptyLine + "# Landing Zones management groups $corpManagementGroupName, $onlineManagementGroupName and `
$sapManagementGroupName created" + $writeSeperatorSpaces + $currentTime) -foregroundcolor $foregroundColor2 $writeEmptyLine

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Move subscriptions under the tenant root group to the correct management groups, if they exist

# Move Management subscription, if it exists
If(!! $subNameManagement)
{
    New-AzManagementGroupSubscription -GroupId $managementManagementGroupGuid -SubscriptionId $subNameManagement.SubscriptionId
}

# Move Connectivity subscription, if it exists
If(!! $subNameConnectivity)
{
    New-AzManagementGroupSubscription -GroupId $connectivityManagementGroupGuid -SubscriptionId $subNameConnectivity.SubscriptionId
}

# Move Identity subscription, if it exists
If(!! $subNameIdentity)
{
    New-AzManagementGroupSubscription -GroupId $identityManagementGroupGuid -SubscriptionId $subNameIdentity.SubscriptionId
}

# Move Corp Production subscription, if it exists
If(!! $subNameCorpPrd)
{
    New-AzManagementGroupSubscription -GroupId $corpManagementGroupGuid  -SubscriptionId $subNameCorpPrd.SubscriptionId
}

# Move Corp Development subscription, if it exists
If(!! $subNameCorpDev)
{
    New-AzManagementGroupSubscription -GroupId $corpManagementGroupGuid  -SubscriptionId $subNameCorpDev.SubscriptionId
}

# Move Online Production subscription, if it exists
If(!! $subNameOnlinePrd)
{
    New-AzManagementGroupSubscription -GroupId $onlineManagementGroupGuid -SubscriptionId $subNameOnlinePrd.SubscriptionId
}

# Move Online Development subscription, if it exists
If(!! $subNameOnlineDev )
{
    New-AzManagementGroupSubscription -GroupId $onlineManagementGroupGuid -SubscriptionId $subNameOnlineDev.SubscriptionId
}

# Move SAP Production subscription, if it exists
If(!! $subNameSapPrd)
{
    New-AzManagementGroupSubscription -GroupID $sapManagementGroupGuid -SubscriptionId $subNameSapPrd.SubscriptionId
}

# Move SAP Development subscription, if it exists
If(!! $subNameSapDev)
{
    New-AzManagementGroupSubscription -GroupID $sapManagementGroupGuid -SubscriptionId $subNameSapDev.SubscriptionId
}

# Move Test subscription, if it exists
If(!! $subNameTest)
{
    New-AzManagementGroupSubscription -GroupId $sandboxesManagementGroupGuid  -SubscriptionId $subNameTest.SubscriptionId
}

Write-Host ($writeEmptyLine + "# Subscriptions moved to management groups" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor2 $writeEmptyLine

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

## Write script completed

Write-Host ($writeEmptyLine + "# Script completed" + $writeSeperatorSpaces + $currentTime)`
-foregroundcolor $foregroundColor1 $writeEmptyLine 

## ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------



When you run the script, your management group tree hierarchy will be structured like shown in the below screenshots.



In a previous blog post I have already shown how you can change the display name of the root management group, which can be combined with this script to foresee a uniform naming convention for all your management groups display names. Next to that, to get a tree overview of your management groups structure, you can follow this Azure tip. And if you want to change the default management group for any newly added subscriptions you can follow the guidelines in this blogpost.


Conclusion

Management groups are a way to proper organize a companies different Azure subscriptions. They bring more structure, by efficiently managing access, policies and costs in an organizational, environment-based or a combined hierarchy.

Management groups can be up to six levels deep, seven if you include the root management group, and a parent management group can have many child management groups. At the moment a total of up to 10,000 management groups is supported.

Just keep in mind that this number is a limit and definitely not a target. You should avoid to complex structures where it is difficult to keep a good overview or where it is almost impossible to find out which Azure Policy definition or RBAC assignment applies to which child or parent management group.

So I hope this Azure PowerShell script is useful for you and provides a good foundation to foresee a structured management group hierarchy in your Azure ecosystem. If you have any questions or recommendations about it, feel free to contact me through my Twitter handle or to leave a comment.


0 comments on “Azure PowerShell script: Create a management group tree hierarchy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: