mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 10:45:02 +01:00
358 lines
18 KiB
PowerShell
358 lines
18 KiB
PowerShell
<#
|
||
.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: "<date> 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"
|
||
}
|