mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
530 lines
34 KiB
PowerShell
530 lines
34 KiB
PowerShell
<#
|
||
.SYNOPSIS
|
||
Generates a hierarchical inventory of Azure Management Groups and Subscriptions.
|
||
|
||
.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 5-level management group structure (Level 0-4).
|
||
|
||
The script provides detailed organizational visibility including:
|
||
- Hierarchical management group structure mapping
|
||
- Subscription placement and organizational context
|
||
- Subscription state tracking (Active, Disabled, etc.)
|
||
- Multi-level governance structure documentation
|
||
- CSV export for organizational analysis and compliance reporting
|
||
|
||
Note: The script is optimized for a maximum 5-level management group depth and starts
|
||
from a configurable root management group ID.
|
||
|
||
.PARAMETER RootManagementGroupId
|
||
The GUID of the root management group to start the hierarchy discovery from.
|
||
Defaults to the Effectory organization root management group.
|
||
|
||
Example: "12345678-1234-1234-1234-123456789012"
|
||
|
||
.OUTPUTS
|
||
CSV File: "<date> azure_managementgroups.csv"
|
||
Contains columns for:
|
||
- Subscription identification (ID, name, state)
|
||
- 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
|
||
|
||
Uses the default Effectory root management group and generates:
|
||
"2024-10-30 1435 azure_managementgroups.csv"
|
||
|
||
.EXAMPLE
|
||
.\ManagementGroups.ps1 -RootManagementGroupId "87654321-4321-4321-4321-210987654321"
|
||
|
||
Discovers management group hierarchy starting from a custom root management group.
|
||
|
||
.EXAMPLE
|
||
.\ManagementGroups.ps1 -RootManagementGroupId "tenant-root-mg" -Verbose
|
||
|
||
Runs with verbose output for detailed discovery logging.
|
||
|
||
.NOTES
|
||
File Name : ManagementGroups.ps1
|
||
Author : Cloud Engineering Team
|
||
Prerequisite : Azure PowerShell module (Az.Resources, Az.Accounts)
|
||
Copyright : (c) 2024 Effectory. All rights reserved.
|
||
|
||
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/
|
||
https://docs.microsoft.com/en-us/powershell/module/az.resources/
|
||
|
||
.COMPONENT
|
||
Requires Azure PowerShell modules:
|
||
- Az.Resources (for management group and subscription enumeration)
|
||
- Az.Accounts (for authentication and context management)
|
||
|
||
.ROLE
|
||
Required Azure permissions:
|
||
- Management Group Reader on the root management group and all child groups
|
||
- Reader access to view subscription details within management groups
|
||
|
||
.FUNCTIONALITY
|
||
- 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
|
||
#>
|
||
|
||
#Requires -Modules Az.Resources, Az.Accounts
|
||
#Requires -Version 5.1
|
||
|
||
# Uncomment the following line if authentication is required
|
||
#Connect-AzAccount
|
||
|
||
[CmdletBinding()]
|
||
param(
|
||
[Parameter(
|
||
Mandatory = $false,
|
||
HelpMessage = "The GUID of the root management group to start hierarchy discovery from"
|
||
)]
|
||
[ValidateNotNullOrEmpty()]
|
||
[string]$RootManagementGroupId = 'e9792fd7-4044-47e7-a40d-3fba46f1cd09'
|
||
)
|
||
|
||
class ResourceCheck {
|
||
[string] $SubscriptionId = ""
|
||
[string] $SubscriptionName = ""
|
||
[string] $SubscriptionState = ""
|
||
[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
|
||
$ErrorActionPreference = "Stop"
|
||
$ProgressPreference = "SilentlyContinue"
|
||
$startTime = Get-Date
|
||
|
||
Write-Host "======================================================================================================================================================================"
|
||
Write-Host "🏗️ AZURE MANAGEMENT GROUP STRUCTURE DISCOVERY" -ForegroundColor Cyan
|
||
Write-Host "======================================================================================================================================================================"
|
||
Write-Host "⏰ Started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')" -ForegroundColor Green
|
||
Write-Host ""
|
||
|
||
try {
|
||
# Validate Azure authentication
|
||
$context = Get-AzContext
|
||
if (-not $context) {
|
||
throw "No Azure context found. Please run Connect-AzAccount first."
|
||
}
|
||
|
||
Write-Host "🔐 Authenticated as: $($context.Account.Id)" -ForegroundColor Green
|
||
Write-Host "🏢 Tenant: $($context.Tenant.Id)" -ForegroundColor Green
|
||
Write-Host "🎯 Root Management Group: $RootManagementGroupId" -ForegroundColor Yellow
|
||
Write-Host ""
|
||
|
||
# Initialize output file and tracking variables
|
||
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
|
||
$fileName = ".\$date azure_managementgroups.csv"
|
||
Write-Host "📄 Output file: $fileName" -ForegroundColor Yellow
|
||
Write-Host ""
|
||
|
||
[ResourceCheck[]]$Result = @()
|
||
$totalSubscriptions = 0
|
||
$managementGroupCount = 0
|
||
|
||
# Get root management group with error handling
|
||
Write-Host "🔍 Discovering root management group structure..." -ForegroundColor Cyan
|
||
$rootManagementGroup = Get-AzManagementGroup -GroupId $RootManagementGroupId -Expand -ErrorAction Stop
|
||
|
||
if (-not $rootManagementGroup) {
|
||
throw "Root management group '$RootManagementGroupId' not found or not accessible."
|
||
}
|
||
|
||
Write-Host "✅ Root Management Group: $($rootManagementGroup.DisplayName)" -ForegroundColor Green
|
||
Write-Host " ID: $($rootManagementGroup.Id)" -ForegroundColor DarkGray
|
||
Write-Host ""
|
||
|
||
# Process Level 0 (Root) subscriptions
|
||
$managementGroupCount++
|
||
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
||
Write-Host "📋 LEVEL 0 (Root): $($rootManagementGroup.DisplayName)" -ForegroundColor Cyan
|
||
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
||
|
||
$subscriptions = $rootManagementGroup.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.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 1 management groups
|
||
$level1Groups = $rootManagementGroup.Children | Where-Object Type -EQ 'Microsoft.Management/managementGroups'
|
||
Write-Host ""
|
||
Write-Host "🔍 Found $($level1Groups.Count) Level 1 management groups" -ForegroundColor Green
|
||
Write-Host ""
|
||
|
||
foreach ($level1ManagementGroupLister in $level1Groups) {
|
||
try {
|
||
$managementGroupCount++
|
||
$level1ManagementGroup = Get-AzManagementGroup -Group $level1ManagementGroupLister.Name -Expand -ErrorAction Stop
|
||
|
||
Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||
Write-Host " 📂 LEVEL 1: $($level1ManagementGroup.DisplayName)" -ForegroundColor Yellow
|
||
Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||
|
||
$subscriptions = $level1ManagementGroup.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.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 1 management group '$($level1ManagementGroupLister.Name)': $($_.Exception.Message)" -ForegroundColor Red
|
||
}
|
||
|
||
# Process Level 2 management groups (nested within Level 1)
|
||
$level2Groups = $level1ManagementGroup.Children | Where-Object Type -EQ 'Microsoft.Management/managementGroups'
|
||
|
||
if ($level2Groups.Count -gt 0) {
|
||
Write-Host " 🔍 Found $($level2Groups.Count) Level 2 management groups" -ForegroundColor Green
|
||
|
||
foreach ($level2ManagementGroupLister in $level2Groups) {
|
||
try {
|
||
$managementGroupCount++
|
||
$level2ManagementGroup = Get-AzManagementGroup -Group $level2ManagementGroupLister.Name -Expand -ErrorAction Stop
|
||
|
||
Write-Host ""
|
||
Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||
Write-Host " 📁 LEVEL 2: $($level2ManagementGroup.DisplayName)" -ForegroundColor Magenta
|
||
Write-Host " ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────"
|
||
|
||
$subscriptions = $level2ManagementGroup.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.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 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
|
||
}
|
||
}
|
||
} else {
|
||
Write-Host " ℹ️ No Level 2 management groups found" -ForegroundColor DarkGray
|
||
}
|
||
}
|
||
|
||
# Export results to CSV
|
||
Write-Host ""
|
||
Write-Host "💾 Exporting results to CSV..." -ForegroundColor Cyan
|
||
$Result | Export-Csv -Path $fileName -NoTypeInformation
|
||
|
||
# Calculate execution time and generate summary report
|
||
$endTime = Get-Date
|
||
$executionTime = $endTime - $startTime
|
||
|
||
Write-Host ""
|
||
Write-Host "======================================================================================================================================================================"
|
||
Write-Host "📊 AZURE MANAGEMENT GROUP DISCOVERY SUMMARY" -ForegroundColor Cyan
|
||
Write-Host "======================================================================================================================================================================"
|
||
Write-Host "⏰ Execution Time: $($executionTime.ToString('hh\:mm\:ss'))" -ForegroundColor Green
|
||
Write-Host "🏗️ Management Groups Discovered: $managementGroupCount" -ForegroundColor Green
|
||
Write-Host "📋 Total Subscriptions Found: $totalSubscriptions" -ForegroundColor Green
|
||
Write-Host "📄 Results Exported To: $fileName" -ForegroundColor Yellow
|
||
|
||
if (Test-Path $fileName) {
|
||
$fileSize = (Get-Item $fileName).Length
|
||
Write-Host "💾 File Size: $([math]::Round($fileSize/1KB, 2)) KB" -ForegroundColor Green
|
||
}
|
||
|
||
# Analyze subscription states
|
||
$subscriptionStates = $Result | Group-Object SubscriptionState
|
||
if ($subscriptionStates.Count -gt 0) {
|
||
Write-Host ""
|
||
Write-Host "📈 SUBSCRIPTION STATE ANALYSIS:" -ForegroundColor Cyan
|
||
foreach ($state in $subscriptionStates) {
|
||
$stateColor = switch ($state.Name) {
|
||
"Enabled" { "Green" }
|
||
"Disabled" { "Red" }
|
||
"Warned" { "Yellow" }
|
||
default { "White" }
|
||
}
|
||
Write-Host " $($state.Name): $($state.Count) subscriptions" -ForegroundColor $stateColor
|
||
}
|
||
}
|
||
|
||
# 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) -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
|
||
Write-Host " 1. Review the generated CSV file for detailed organizational mapping" -ForegroundColor White
|
||
Write-Host " 2. Analyze subscription placement for governance compliance" -ForegroundColor White
|
||
Write-Host " 3. Consider moving orphaned subscriptions to appropriate management groups" -ForegroundColor White
|
||
Write-Host " 4. Use this data for policy assignment and resource organization planning" -ForegroundColor White
|
||
|
||
Write-Host ""
|
||
Write-Host "✅ Azure Management Group discovery completed successfully!" -ForegroundColor Green
|
||
Write-Host "======================================================================================================================================================================"
|
||
|
||
} catch {
|
||
Write-Host ""
|
||
Write-Host "❌ CRITICAL ERROR OCCURRED" -ForegroundColor Red
|
||
Write-Host "======================================================================================================================================================================"
|
||
Write-Host "Error: $($_.Exception.Message)" -ForegroundColor Red
|
||
Write-Host "Line: $($_.InvocationInfo.ScriptLineNumber)" -ForegroundColor Red
|
||
Write-Host "Position: $($_.InvocationInfo.OffsetInLine)" -ForegroundColor Red
|
||
Write-Host ""
|
||
Write-Host "🔧 TROUBLESHOOTING STEPS:" -ForegroundColor Yellow
|
||
Write-Host " 1. Verify you are authenticated to Azure (Connect-AzAccount)" -ForegroundColor White
|
||
Write-Host " 2. Ensure you have Management Group Reader permissions on the root management group" -ForegroundColor White
|
||
Write-Host " 3. Verify the root management group ID '$RootManagementGroupId' exists and is accessible" -ForegroundColor White
|
||
Write-Host " 4. Check that Azure PowerShell modules are installed and up to date" -ForegroundColor White
|
||
Write-Host " 5. Try running with a different root management group ID if the current one is invalid" -ForegroundColor White
|
||
Write-Host ""
|
||
Write-Host "📞 For additional support, contact the Cloud Engineering team" -ForegroundColor Cyan
|
||
Write-Host "======================================================================================================================================================================"
|
||
|
||
# Ensure we exit with error code for automation scenarios
|
||
exit 1
|
||
} finally {
|
||
# Reset progress preference
|
||
$ProgressPreference = "Continue"
|
||
}
|
||
|