mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
Merged PR 64828: updated management groups overview with extra levels for the cost overview
updated management groups overview with extra levels for the cost overview #125630 Related work items: #125630
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
.DESCRIPTION
|
.DESCRIPTION
|
||||||
This script creates a comprehensive mapping of Azure organizational structure by traversing
|
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
|
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:
|
The script provides detailed organizational visibility including:
|
||||||
- Hierarchical management group structure mapping
|
- Hierarchical management group structure mapping
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
- Multi-level governance structure documentation
|
- Multi-level governance structure documentation
|
||||||
- CSV export for organizational analysis and compliance reporting
|
- 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.
|
from a configurable root management group ID.
|
||||||
|
|
||||||
.PARAMETER RootManagementGroupId
|
.PARAMETER RootManagementGroupId
|
||||||
@@ -30,6 +30,8 @@
|
|||||||
- Level 0 Management Group (root level)
|
- Level 0 Management Group (root level)
|
||||||
- Level 1 Management Group (department/division level)
|
- Level 1 Management Group (department/division level)
|
||||||
- Level 2 Management Group (team/project level)
|
- Level 2 Management Group (team/project level)
|
||||||
|
- Level 3 Management Group (workload/application level)
|
||||||
|
- Level 4 Management Group (environment/instance level)
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
.\ManagementGroups.ps1
|
.\ManagementGroups.ps1
|
||||||
@@ -56,6 +58,8 @@
|
|||||||
Version History:
|
Version History:
|
||||||
1.0 - Initial release with 3-level management group hierarchy discovery
|
1.0 - Initial release with 3-level management group hierarchy discovery
|
||||||
1.1 - Added parameterized root management group for flexibility
|
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
|
.LINK
|
||||||
https://docs.microsoft.com/en-us/azure/governance/management-groups/
|
https://docs.microsoft.com/en-us/azure/governance/management-groups/
|
||||||
@@ -72,7 +76,7 @@
|
|||||||
- Reader access to view subscription details within management groups
|
- Reader access to view subscription details within management groups
|
||||||
|
|
||||||
.FUNCTIONALITY
|
.FUNCTIONALITY
|
||||||
- Hierarchical management group discovery (3-level maximum)
|
- Hierarchical management group discovery (5-level maximum)
|
||||||
- Subscription placement mapping and state tracking
|
- Subscription placement mapping and state tracking
|
||||||
- Organizational structure documentation and CSV export
|
- Organizational structure documentation and CSV export
|
||||||
- Cross-tenant compatible with configurable root management group
|
- Cross-tenant compatible with configurable root management group
|
||||||
@@ -101,9 +105,13 @@ class ResourceCheck {
|
|||||||
[string] $Level0_ManagementGroupId = ""
|
[string] $Level0_ManagementGroupId = ""
|
||||||
[string] $Level1_ManagementGroupId = ""
|
[string] $Level1_ManagementGroupId = ""
|
||||||
[string] $Level2_ManagementGroupId = ""
|
[string] $Level2_ManagementGroupId = ""
|
||||||
|
[string] $Level3_ManagementGroupId = ""
|
||||||
|
[string] $Level4_ManagementGroupId = ""
|
||||||
[string] $Level0_ManagementGroupName = ""
|
[string] $Level0_ManagementGroupName = ""
|
||||||
[string] $Level1_ManagementGroupName = ""
|
[string] $Level1_ManagementGroupName = ""
|
||||||
[string] $Level2_ManagementGroupName = ""
|
[string] $Level2_ManagementGroupName = ""
|
||||||
|
[string] $Level3_ManagementGroupName = ""
|
||||||
|
[string] $Level4_ManagementGroupName = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
# Initialize script execution
|
# 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 {
|
} catch {
|
||||||
Write-Host " ❌ Error accessing Level 2 management group '$($level2ManagementGroupLister.Name)': $($_.Exception.Message)" -ForegroundColor Red
|
Write-Host " ❌ Error accessing Level 2 management group '$($level2ManagementGroupLister.Name)': $($_.Exception.Message)" -ForegroundColor Red
|
||||||
}
|
}
|
||||||
@@ -345,13 +479,17 @@ try {
|
|||||||
# Provide organizational insights
|
# Provide organizational insights
|
||||||
$level0Subs = ($Result | Where-Object { [string]::IsNullOrEmpty($_.Level1_ManagementGroupId) }).Count
|
$level0Subs = ($Result | Where-Object { [string]::IsNullOrEmpty($_.Level1_ManagementGroupId) }).Count
|
||||||
$level1Subs = ($Result | Where-Object { -not [string]::IsNullOrEmpty($_.Level1_ManagementGroupId) -and [string]::IsNullOrEmpty($_.Level2_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 ""
|
||||||
Write-Host "🏗️ ORGANIZATIONAL STRUCTURE:" -ForegroundColor Cyan
|
Write-Host "🏗️ ORGANIZATIONAL STRUCTURE:" -ForegroundColor Cyan
|
||||||
Write-Host " Root Level (Level 0): $level0Subs subscriptions" -ForegroundColor Green
|
Write-Host " Root Level (Level 0): $level0Subs subscriptions" -ForegroundColor Green
|
||||||
Write-Host " Department/Division Level (Level 1): $level1Subs subscriptions" -ForegroundColor Yellow
|
Write-Host " Department/Division Level (Level 1): $level1Subs subscriptions" -ForegroundColor Yellow
|
||||||
Write-Host " Team/Project Level (Level 2): $level2Subs subscriptions" -ForegroundColor Magenta
|
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 ""
|
||||||
Write-Host "📈 NEXT STEPS:" -ForegroundColor Cyan
|
Write-Host "📈 NEXT STEPS:" -ForegroundColor Cyan
|
||||||
|
|||||||
Reference in New Issue
Block a user