<# .SYNOPSIS Generates a comprehensive inventory of all Azure resources across enabled subscriptions. .DESCRIPTION This script enumerates all Azure resources within enabled subscriptions across the entire Azure tenant, collecting detailed resource properties, governance tags, and managed identity information. The results are exported to a timestamped CSV file for analysis, compliance reporting, and resource management. Key capabilities: - Cross-subscription resource discovery and enumeration - Comprehensive resource metadata collection (type, location, resource group) - Governance tag extraction for team ownership and compliance tracking - Managed identity discovery and principal ID mapping - Resource lifecycle management tags (delete, split, deployment tracking) - Timestamped CSV export with complete audit trail The script processes all enabled subscriptions and captures essential resource information including resource hierarchy, governance tags, and security identities for comprehensive Azure estate management and reporting. .PARAMETER None This script does not accept parameters and will process all resources in all enabled subscriptions. .OUTPUTS CSV File: " Resources.csv" Contains columns for: - Resource identification (ID, name, type, kind, location) - Subscription and resource group context - Governance tags (team, product, environment, data classification) - Lifecycle management tags (delete, split, created date, deployment) - Managed identity information (name, principal ID) .EXAMPLE .\Resources.ps1 Discovers all resources across enabled subscriptions and generates: "2024-10-30 1435 Resources.csv" .NOTES File Name : Resources.ps1 Author : Cloud Engineering Team Prerequisite : Azure PowerShell module (Az.Resources, Az.Accounts, Az.ManagedServiceIdentity) Copyright : (c) 2024 Effectory. All rights reserved. Version History: 1.0 - Initial release with comprehensive resource inventory functionality .LINK https://docs.microsoft.com/en-us/azure/azure-resource-manager/management/overview https://docs.microsoft.com/en-us/powershell/module/az.resources/ .COMPONENT Requires Azure PowerShell modules: - Az.Resources (for resource enumeration and property retrieval) - Az.Accounts (for authentication and subscription management) - Az.ManagedServiceIdentity (for managed identity discovery) .ROLE Required Azure permissions: - Reader or higher on all target subscriptions - Managed Identity Operator (for identity information retrieval) .FUNCTIONALITY - Multi-subscription resource discovery and enumeration - Governance tag extraction and compliance tracking - Managed identity mapping and security context analysis - CSV export with comprehensive resource metadata #> #Requires -Modules Az.Resources, Az.Accounts, Az.ManagedServiceIdentity #Requires -Version 5.1 [CmdletBinding()] param() # Uncomment the following line if authentication is required #Connect-AzAccount class ResourceCheck { [string] $ResourceId = "" [string] $Id = "" [string] $Kind = "" [string] $Location = "" [string] $ResourceName = "" [string] $ResourceGroupName = "" [string] $ResourceType = "" [string] $SubscriptionId = "" [string] $SubscriptionName = "" [string] $Tag_Team = "" [string] $Tag_Product = "" [string] $Tag_Environment = "" [string] $Tag_Data = "" [string] $Tag_Delete = "" [string] $Tag_Split = "" [string] $Tag_CreatedOnDate = "" [string] $Tag_Deployment = "" [string] $ManagedIndentity_Name = "" [string] $ManagedIndentity_PrincipalId = "" } # Initialize script execution $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $startTime = Get-Date Write-Host "======================================================================================================================================================================" Write-Host "🔍 AZURE RESOURCE INVENTORY GENERATOR" -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 "" # Get enabled subscriptions Write-Host "🔄 Discovering enabled subscriptions..." -ForegroundColor Cyan $subscriptions = Get-AzSubscription | Where-Object State -eq "Enabled" Write-Host "✅ Found $($subscriptions.Count) enabled subscriptions" -ForegroundColor Green Write-Host "" # Initialize output file and tracking variables [string] $date = Get-Date -Format "yyyy-MM-dd HHmm" $fileName = ".\$date Resources.csv" Write-Host "📄 Output file: $fileName" -ForegroundColor Yellow Write-Host "" $totalResources = 0 $processedSubscriptions = 0 $resourceTypes = @{} $managedIdentityCount = 0 $governanceIssues = @() # Process each subscription foreach ($subscription in $subscriptions) { $processedSubscriptions++ Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------" Write-Host "🔄 Processing Subscription [$($processedSubscriptions)/$($subscriptions.Count)]: $($subscription.Name)" -ForegroundColor Yellow Write-Host " ID: $($subscription.Id)" -ForegroundColor DarkGray try { Set-AzContext -SubscriptionId $subscription.Id | Out-Null # Get all resources in the subscription Write-Host " 🔍 Discovering resources..." -ForegroundColor Cyan $allResources = Get-AzResource -ErrorAction Stop Write-Host " ✅ Found $($allResources.Count) resources" -ForegroundColor Green [ResourceCheck[]]$Result = @() $subscriptionResourceCount = 0 $subscriptionManagedIdentityCount = 0 foreach ($resource in $allResources) { try { $subscriptionResourceCount++ # Track resource types for analytics if ($resourceTypes.ContainsKey($resource.ResourceType)) { $resourceTypes[$resource.ResourceType]++ } else { $resourceTypes[$resource.ResourceType] = 1 } # Create resource check object [ResourceCheck] $resourceCheck = [ResourceCheck]::new() $resourceCheck.ResourceId = $resource.ResourceId $resourceCheck.Id = $resource.Id $resourceCheck.Kind = $resource.Kind $resourceCheck.Location = $resource.Location $resourceCheck.ResourceName = $resource.ResourceName $resourceCheck.ResourceGroupName = $resource.ResourceGroupName $resourceCheck.ResourceType = $resource.ResourceType $resourceCheck.SubscriptionId = $subscription.Id $resourceCheck.SubscriptionName = $subscription.Name $resourceCheck.Tag_Team = $resource.Tags.team $resourceCheck.Tag_Product = $resource.Tags.product $resourceCheck.Tag_Environment = $resource.Tags.environment $resourceCheck.Tag_Data = $resource.Tags.data $resourceCheck.Tag_Delete = $resource.Tags.delete $resourceCheck.Tag_Split = $resource.Tags.split $resourceCheck.Tag_CreatedOnDate = $resource.Tags.CreatedOnDate $resourceCheck.Tag_Deployment = $resource.Tags.drp_deployment # Check for governance tag compliance $missingTags = @() if ([string]::IsNullOrEmpty($resource.Tags.team)) { $missingTags += "team" } if ([string]::IsNullOrEmpty($resource.Tags.product)) { $missingTags += "product" } if ([string]::IsNullOrEmpty($resource.Tags.environment)) { $missingTags += "environment" } if ($missingTags.Count -gt 0) { $governanceIssues += "⚠️ Missing tags [$($missingTags -join ', ')] on $($resource.ResourceType): $($resource.ResourceName) in $($resource.ResourceGroupName)" } # Attempt to get managed identity information try { $managedIdentity = Get-AzSystemAssignedIdentity -Scope $resource.ResourceId -ErrorAction SilentlyContinue if ($managedIdentity) { $resourceCheck.ManagedIndentity_Name = $managedIdentity.Name $resourceCheck.ManagedIndentity_PrincipalId = $managedIdentity.PrincipalId $subscriptionManagedIdentityCount++ $managedIdentityCount++ } else { $resourceCheck.ManagedIndentity_Name = "" $resourceCheck.ManagedIndentity_PrincipalId = "" } } catch { # Silently handle managed identity lookup failures $resourceCheck.ManagedIndentity_Name = "" $resourceCheck.ManagedIndentity_PrincipalId = "" } $Result += $resourceCheck $totalResources++ # Show progress for large subscriptions if ($subscriptionResourceCount % 100 -eq 0) { Write-Host " 📊 Processed $subscriptionResourceCount resources..." -ForegroundColor DarkCyan } } catch { Write-Host " ❌ Error processing resource '$($resource.ResourceName)': $($_.Exception.Message)" -ForegroundColor Red } } # Export results for this subscription if ($Result.Count -gt 0) { $Result | Export-Csv -Path $fileName -Append -NoTypeInformation Write-Host " ✅ Exported $($Result.Count) resources" -ForegroundColor Green if ($subscriptionManagedIdentityCount -gt 0) { Write-Host " 🔐 Found $subscriptionManagedIdentityCount managed identities" -ForegroundColor Cyan } } else { Write-Host " ℹ️ No resources found in subscription" -ForegroundColor DarkYellow } } catch { Write-Host " ❌ Error processing subscription: $($_.Exception.Message)" -ForegroundColor Red } Write-Host "" } # Calculate execution time and generate comprehensive summary report $endTime = Get-Date $executionTime = $endTime - $startTime Write-Host "======================================================================================================================================================================" Write-Host "📊 AZURE RESOURCE INVENTORY SUMMARY" -ForegroundColor Cyan Write-Host "======================================================================================================================================================================" Write-Host "⏰ Execution Time: $($executionTime.ToString('hh\:mm\:ss'))" -ForegroundColor Green Write-Host "📋 Subscriptions Processed: $processedSubscriptions" -ForegroundColor Green Write-Host "🔍 Total Resources Discovered: $totalResources" -ForegroundColor Green Write-Host "🔐 Managed Identities Found: $managedIdentityCount" -ForegroundColor Cyan 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 } # Display top resource types if ($resourceTypes.Count -gt 0) { Write-Host "" Write-Host "📈 TOP RESOURCE TYPES:" -ForegroundColor Cyan $topResourceTypes = $resourceTypes.GetEnumerator() | Sort-Object Value -Descending | Select-Object -First 10 foreach ($resourceType in $topResourceTypes) { Write-Host " $($resourceType.Key): $($resourceType.Value) resources" -ForegroundColor White } if ($resourceTypes.Count -gt 10) { $remainingTypes = $resourceTypes.Count - 10 $remainingResources = ($resourceTypes.GetEnumerator() | Sort-Object Value -Descending | Select-Object -Skip 10 | Measure-Object Value -Sum).Sum Write-Host " ... and $remainingTypes more types ($remainingResources resources)" -ForegroundColor DarkGray } } # Display governance analysis if ($governanceIssues.Count -gt 0) { Write-Host "" Write-Host "🚨 GOVERNANCE ANALYSIS" -ForegroundColor Red Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------" Write-Host "Found $($governanceIssues.Count) resources with missing governance tags:" -ForegroundColor Yellow foreach ($issue in $governanceIssues | Select-Object -First 15) { Write-Host " $issue" -ForegroundColor Yellow } if ($governanceIssues.Count -gt 15) { Write-Host " ... and $($governanceIssues.Count - 15) more governance issues (see CSV for complete details)" -ForegroundColor DarkYellow } Write-Host "" Write-Host "📋 Governance Recommendations:" -ForegroundColor Cyan Write-Host " • Implement mandatory tagging policies for team, product, and environment" -ForegroundColor White Write-Host " • Use Azure Policy to enforce governance tag compliance" -ForegroundColor White Write-Host " • Establish resource naming conventions and tagging standards" -ForegroundColor White Write-Host " • Regular governance audits using this resource inventory" -ForegroundColor White } else { Write-Host "" Write-Host "✅ GOVERNANCE ANALYSIS: All resources have required governance tags" -ForegroundColor Green } # Security and identity insights if ($managedIdentityCount -gt 0) { $identityPercentage = [math]::Round(($managedIdentityCount / $totalResources) * 100, 1) Write-Host "" Write-Host "🔐 SECURITY ANALYSIS:" -ForegroundColor Cyan Write-Host " Managed Identity Adoption: $identityPercentage% of resources ($managedIdentityCount/$totalResources)" -ForegroundColor Green Write-Host " 💡 Consider expanding managed identity usage for enhanced security" -ForegroundColor White } Write-Host "" Write-Host "📈 NEXT STEPS:" -ForegroundColor Cyan Write-Host " 1. Review the generated CSV file for detailed resource analysis" -ForegroundColor White Write-Host " 2. Address governance tag compliance issues identified above" -ForegroundColor White Write-Host " 3. Analyze resource distribution across subscriptions and regions" -ForegroundColor White Write-Host " 4. Consider resource optimization opportunities (unused resources, right-sizing)" -ForegroundColor White Write-Host " 5. Implement automated resource monitoring and cost management" -ForegroundColor White Write-Host " 6. Use managed identity information for security auditing" -ForegroundColor White Write-Host "" Write-Host "✅ Azure resource inventory 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 Reader permissions on all target subscriptions" -ForegroundColor White Write-Host " 3. Check that Azure PowerShell modules are installed and up to date" -ForegroundColor White Write-Host " 4. Verify Managed Identity Operator role for identity information retrieval" -ForegroundColor White Write-Host " 5. Try running the script with -Verbose for additional diagnostic information" -ForegroundColor White Write-Host " 6. Consider processing subscriptions individually if encountering timeout issues" -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" }