From 0212b4b98b7cd62c1e320077252b5f09802d4008 Mon Sep 17 00:00:00 2001 From: Jurjen Ladenius Date: Fri, 19 Dec 2025 10:41:15 +0100 Subject: [PATCH] updated management groups overview with extra levels for the cost overview #125630 --- Powershell/Lists/Azure/ManagementGroups.ps1 | 146 +++++++++++++++++++- 1 file changed, 142 insertions(+), 4 deletions(-) diff --git a/Powershell/Lists/Azure/ManagementGroups.ps1 b/Powershell/Lists/Azure/ManagementGroups.ps1 index dffded9..fd83fad 100644 --- a/Powershell/Lists/Azure/ManagementGroups.ps1 +++ b/Powershell/Lists/Azure/ManagementGroups.ps1 @@ -5,7 +5,7 @@ .DESCRIPTION This script creates a comprehensive mapping of Azure organizational structure by traversing the management group hierarchy starting from a specified root management group. It discovers - and documents all subscriptions within a 3-level management group structure (Level 0-2). + and documents all subscriptions within a 5-level management group structure (Level 0-4). The script provides detailed organizational visibility including: - Hierarchical management group structure mapping @@ -14,7 +14,7 @@ - Multi-level governance structure documentation - CSV export for organizational analysis and compliance reporting - Note: The script is optimized for a maximum 3-level management group depth and starts + Note: The script is optimized for a maximum 5-level management group depth and starts from a configurable root management group ID. .PARAMETER RootManagementGroupId @@ -30,6 +30,8 @@ - Level 0 Management Group (root level) - Level 1 Management Group (department/division level) - Level 2 Management Group (team/project level) + - Level 3 Management Group (workload/application level) + - Level 4 Management Group (environment/instance level) .EXAMPLE .\ManagementGroups.ps1 @@ -56,6 +58,8 @@ Version History: 1.0 - Initial release with 3-level management group hierarchy discovery 1.1 - Added parameterized root management group for flexibility + 1.2 - Extended to 4-level management group hierarchy (added Level 3) + 1.3 - Extended to 5-level management group hierarchy (added Level 4) .LINK https://docs.microsoft.com/en-us/azure/governance/management-groups/ @@ -72,7 +76,7 @@ - Reader access to view subscription details within management groups .FUNCTIONALITY - - Hierarchical management group discovery (3-level maximum) + - Hierarchical management group discovery (5-level maximum) - Subscription placement mapping and state tracking - Organizational structure documentation and CSV export - Cross-tenant compatible with configurable root management group @@ -101,9 +105,13 @@ class ResourceCheck { [string] $Level0_ManagementGroupId = "" [string] $Level1_ManagementGroupId = "" [string] $Level2_ManagementGroupId = "" + [string] $Level3_ManagementGroupId = "" + [string] $Level4_ManagementGroupId = "" [string] $Level0_ManagementGroupName = "" [string] $Level1_ManagementGroupName = "" [string] $Level2_ManagementGroupName = "" + [string] $Level3_ManagementGroupName = "" + [string] $Level4_ManagementGroupName = "" } # Initialize script execution @@ -294,6 +302,132 @@ try { } } + # Process Level 3 management groups (nested within Level 2) + $level3Groups = $level2ManagementGroup.Children | Where-Object Type -EQ 'Microsoft.Management/managementGroups' + + if ($level3Groups.Count -gt 0) { + Write-Host " 🔍 Found $($level3Groups.Count) Level 3 management groups" -ForegroundColor Green + + foreach ($level3ManagementGroupLister in $level3Groups) { + try { + $managementGroupCount++ + $level3ManagementGroup = Get-AzManagementGroup -Group $level3ManagementGroupLister.Name -Expand -ErrorAction Stop + + Write-Host "" + Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────" + Write-Host " 📁 LEVEL 3: $($level3ManagementGroup.DisplayName)" -ForegroundColor DarkCyan + Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────" + + $subscriptions = $level3ManagementGroup.Children | Where-Object Type -EQ '/subscriptions' + Write-Host " 📊 Direct subscriptions: $($subscriptions.Count)" -ForegroundColor Green + + foreach ($subscription in $subscriptions) { + try { + $scope = $subscription.Id.Substring($subscription.Parent.Length, $subscription.Id.Length - $subscription.Parent.Length) + $subscriptionId = $scope.Replace("/subscriptions/", "") + + # Color code subscription state + $stateColor = switch ($subscription.State) { + "Enabled" { "Green" } + "Disabled" { "Red" } + "Warned" { "Yellow" } + default { "White" } + } + + Write-Host " 📋 Subscription: $($subscription.DisplayName) [$subscriptionId] - $($subscription.State)" -ForegroundColor $stateColor + + [ResourceCheck] $resourceCheck = [ResourceCheck]::new() + $resourceCheck.Level0_ManagementGroupId = $rootManagementGroup.Id + $resourceCheck.Level0_ManagementGroupName = $rootManagementGroup.DisplayName + $resourceCheck.Level1_ManagementGroupId = $level1ManagementGroup.Id + $resourceCheck.Level1_ManagementGroupName = $level1ManagementGroup.DisplayName + $resourceCheck.Level2_ManagementGroupId = $level2ManagementGroup.Id + $resourceCheck.Level2_ManagementGroupName = $level2ManagementGroup.DisplayName + $resourceCheck.Level3_ManagementGroupId = $level3ManagementGroup.Id + $resourceCheck.Level3_ManagementGroupName = $level3ManagementGroup.DisplayName + $resourceCheck.SubscriptionId = $subscriptionId + $resourceCheck.SubscriptionName = $subscription.DisplayName + $resourceCheck.SubscriptionState = $subscription.State + $Result += $resourceCheck + $totalSubscriptions++ + + } catch { + Write-Host " ❌ Error processing subscription: $($_.Exception.Message)" -ForegroundColor Red + } + } + + # Process Level 4 management groups (nested within Level 3) + $level4Groups = $level3ManagementGroup.Children | Where-Object Type -EQ 'Microsoft.Management/managementGroups' + + if ($level4Groups.Count -gt 0) { + Write-Host " 🔍 Found $($level4Groups.Count) Level 4 management groups" -ForegroundColor Green + + foreach ($level4ManagementGroupLister in $level4Groups) { + try { + $managementGroupCount++ + $level4ManagementGroup = Get-AzManagementGroup -Group $level4ManagementGroupLister.Name -Expand -ErrorAction Stop + + Write-Host "" + Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────" + Write-Host " 📂 LEVEL 4: $($level4ManagementGroup.DisplayName)" -ForegroundColor Blue + Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────" + + $subscriptions = $level4ManagementGroup.Children | Where-Object Type -EQ '/subscriptions' + Write-Host " 📊 Direct subscriptions: $($subscriptions.Count)" -ForegroundColor Green + + foreach ($subscription in $subscriptions) { + try { + $scope = $subscription.Id.Substring($subscription.Parent.Length, $subscription.Id.Length - $subscription.Parent.Length) + $subscriptionId = $scope.Replace("/subscriptions/", "") + + # Color code subscription state + $stateColor = switch ($subscription.State) { + "Enabled" { "Green" } + "Disabled" { "Red" } + "Warned" { "Yellow" } + default { "White" } + } + + Write-Host " 📋 Subscription: $($subscription.DisplayName) [$subscriptionId] - $($subscription.State)" -ForegroundColor $stateColor + + [ResourceCheck] $resourceCheck = [ResourceCheck]::new() + $resourceCheck.Level0_ManagementGroupId = $rootManagementGroup.Id + $resourceCheck.Level0_ManagementGroupName = $rootManagementGroup.DisplayName + $resourceCheck.Level1_ManagementGroupId = $level1ManagementGroup.Id + $resourceCheck.Level1_ManagementGroupName = $level1ManagementGroup.DisplayName + $resourceCheck.Level2_ManagementGroupId = $level2ManagementGroup.Id + $resourceCheck.Level2_ManagementGroupName = $level2ManagementGroup.DisplayName + $resourceCheck.Level3_ManagementGroupId = $level3ManagementGroup.Id + $resourceCheck.Level3_ManagementGroupName = $level3ManagementGroup.DisplayName + $resourceCheck.Level4_ManagementGroupId = $level4ManagementGroup.Id + $resourceCheck.Level4_ManagementGroupName = $level4ManagementGroup.DisplayName + $resourceCheck.SubscriptionId = $subscriptionId + $resourceCheck.SubscriptionName = $subscription.DisplayName + $resourceCheck.SubscriptionState = $subscription.State + $Result += $resourceCheck + $totalSubscriptions++ + + } catch { + Write-Host " ❌ Error processing subscription: $($_.Exception.Message)" -ForegroundColor Red + } + } + + } catch { + Write-Host " ❌ Error accessing Level 4 management group '$($level4ManagementGroupLister.Name)': $($_.Exception.Message)" -ForegroundColor Red + } + } + } else { + Write-Host " â„šī¸ No Level 4 management groups found" -ForegroundColor DarkGray + } + + } catch { + Write-Host " ❌ Error accessing Level 3 management group '$($level3ManagementGroupLister.Name)': $($_.Exception.Message)" -ForegroundColor Red + } + } + } else { + Write-Host " â„šī¸ No Level 3 management groups found" -ForegroundColor DarkGray + } + } catch { Write-Host " ❌ Error accessing Level 2 management group '$($level2ManagementGroupLister.Name)': $($_.Exception.Message)" -ForegroundColor Red } @@ -345,13 +479,17 @@ try { # Provide organizational insights $level0Subs = ($Result | Where-Object { [string]::IsNullOrEmpty($_.Level1_ManagementGroupId) }).Count $level1Subs = ($Result | Where-Object { -not [string]::IsNullOrEmpty($_.Level1_ManagementGroupId) -and [string]::IsNullOrEmpty($_.Level2_ManagementGroupId) }).Count - $level2Subs = ($Result | Where-Object { -not [string]::IsNullOrEmpty($_.Level2_ManagementGroupId) }).Count + $level2Subs = ($Result | Where-Object { -not [string]::IsNullOrEmpty($_.Level2_ManagementGroupId) -and [string]::IsNullOrEmpty($_.Level3_ManagementGroupId) }).Count + $level3Subs = ($Result | Where-Object { -not [string]::IsNullOrEmpty($_.Level3_ManagementGroupId) -and [string]::IsNullOrEmpty($_.Level4_ManagementGroupId) }).Count + $level4Subs = ($Result | Where-Object { -not [string]::IsNullOrEmpty($_.Level4_ManagementGroupId) }).Count Write-Host "" Write-Host "đŸ—ī¸ ORGANIZATIONAL STRUCTURE:" -ForegroundColor Cyan Write-Host " Root Level (Level 0): $level0Subs subscriptions" -ForegroundColor Green Write-Host " Department/Division Level (Level 1): $level1Subs subscriptions" -ForegroundColor Yellow Write-Host " Team/Project Level (Level 2): $level2Subs subscriptions" -ForegroundColor Magenta + Write-Host " Workload/Application Level (Level 3): $level3Subs subscriptions" -ForegroundColor DarkCyan + Write-Host " Environment/Instance Level (Level 4): $level4Subs subscriptions" -ForegroundColor Blue Write-Host "" Write-Host "📈 NEXT STEPS:" -ForegroundColor Cyan