mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 18:52:18 +01:00
added documetation
This commit is contained in:
@@ -1,4 +1,81 @@
|
||||
#Connect-AzAccount
|
||||
<#
|
||||
.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 = ""
|
||||
@@ -22,59 +99,259 @@ class ResourceCheck {
|
||||
[string] $ManagedIndentity_PrincipalId = ""
|
||||
}
|
||||
|
||||
Write-Host "========================================================================================================================================================================"
|
||||
Write-Host "Creating resource overview."
|
||||
Write-Host "========================================================================================================================================================================"
|
||||
# Initialize script execution
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
$startTime = Get-Date
|
||||
|
||||
$subscriptions = Get-AzSubscription | Where-Object State -eq "Enabled"
|
||||
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 ""
|
||||
|
||||
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
|
||||
$fileName = ".\$date Resources.csv"
|
||||
|
||||
foreach ($subscription in $subscriptions)
|
||||
{
|
||||
Set-AzContext -SubscriptionId $subscription.Id
|
||||
|
||||
$allResources = Get-AzResource
|
||||
[ResourceCheck[]]$Result = @()
|
||||
try {
|
||||
# Validate Azure authentication
|
||||
$context = Get-AzContext
|
||||
if (-not $context) {
|
||||
throw "No Azure context found. Please run Connect-AzAccount first."
|
||||
}
|
||||
|
||||
foreach ($resource in $allResources)
|
||||
{
|
||||
[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
|
||||
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 {
|
||||
$managedIdentity = $null
|
||||
$managedIdentity = Get-AzSystemAssignedIdentity -Scope $resource.ResourceId -erroraction 'silentlycontinue'
|
||||
$resourceCheck.ManagedIndentity_Name = $managedIdentity.Name
|
||||
$resourceCheck.ManagedIndentity_PrincipalId = $managedIdentity.PrincipalId
|
||||
}
|
||||
catch {
|
||||
$resourceCheck.ManagedIndentity_Name = ""
|
||||
$resourceCheck.ManagedIndentity_PrincipalId = ""
|
||||
}
|
||||
Set-AzContext -SubscriptionId $subscription.Id | Out-Null
|
||||
|
||||
$Result += $resourceCheck
|
||||
# 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 ""
|
||||
}
|
||||
|
||||
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
|
||||
}
|
||||
# Calculate execution time and generate comprehensive summary report
|
||||
$endTime = Get-Date
|
||||
$executionTime = $endTime - $startTime
|
||||
|
||||
Write-Host "========================================================================================================================================================================"
|
||||
Write-Host "Done."
|
||||
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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user