<# .SYNOPSIS Generates a comprehensive inventory of Azure Key Vaults across all management groups and subscriptions. .DESCRIPTION This script enumerates all Azure Key Vaults within enabled subscriptions across the entire Azure tenant, collecting detailed configuration properties, security settings, and governance tags. The results are exported to a timestamped CSV file for analysis, compliance reporting, and security auditing. Key capabilities: - Multi-tenant Key Vault discovery across all management groups - Configuration analysis including RBAC, purge protection, and soft delete settings - Governance tag extraction for team ownership and compliance tracking - Public network access configuration reporting - Timestamped CSV export for audit trails and trend analysis .PARAMETER None This script does not accept parameters and will process all accessible Key Vaults. .OUTPUTS CSV File: " azure_key_vaults.csv" Contains columns for: - Resource identification (ID, name, resource group, location) - Management hierarchy (management group, subscription) - Governance tags (team, product, environment, data classification) - Security configuration (RBAC, purge protection, soft delete, network access) .EXAMPLE .\KeyVaults.ps1 Discovers all Key Vaults and generates: "2024-10-30 1435 azure_key_vaults.csv" .NOTES File Name : KeyVaults.ps1 Author : Cloud Engineering Team Prerequisite : Azure PowerShell module (Az.KeyVault, Az.Resources, Az.Accounts) Copyright : (c) 2024 Effectory. All rights reserved. Version History: 1.0 - Initial release with comprehensive Key Vault inventory functionality .LINK https://docs.microsoft.com/en-us/azure/key-vault/ https://docs.microsoft.com/en-us/powershell/module/az.keyvault/ .COMPONENT Requires Azure PowerShell modules: - Az.KeyVault (for Key Vault enumeration and property retrieval) - Az.Resources (for resource group and management group access) - Az.Accounts (for authentication and subscription management) .ROLE Required Azure permissions: - Key Vault Reader or higher on all subscriptions - Management Group Reader for organizational hierarchy access .FUNCTIONALITY - Multi-subscription Key Vault discovery - Security configuration analysis and reporting - Governance tag extraction and compliance tracking - CSV export with comprehensive audit trail #> #Requires -Modules Az.KeyVault, Az.Resources, Az.Accounts #Requires -Version 5.1 [CmdletBinding()] param() # Uncomment the following line if authentication is required #Connect-AzAccount class ResourceCheck { [string] $ResourceId = "" [string] $ManagementGroupId = "" [string] $ManagementGroupName = "" [string] $SubscriptionId = "" [string] $SubscriptionName = "" [string] $ResourceGroup = "" [string] $ResourceName = "" [string] $Location = "" [string] $Tag_Team = "" [string] $Tag_Product = "" [string] $Tag_Environment = "" [string] $Tag_Data = "" [string] $Tag_Deployment = "" [string] $Tag_CreatedOnDate = "" [string] $Prop_EnablePurgeProtection = "" [string] $Prop_EnableRbacAuthorization = "" [string] $Prop_EnableSoftDelete = "" [string] $Prop_PublicNetworkAccess = "" } # Initialize script execution $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $startTime = Get-Date Write-Host "======================================================================================================================================================================" Write-Host "🔐 AZURE KEY VAULT 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 "" # Initialize output file [string] $date = Get-Date -Format "yyyy-MM-dd HHmm" $fileName = ".\$date azure_key_vaults.csv" Write-Host "📄 Output file: $fileName" -ForegroundColor Yellow Write-Host "" # Get management groups for organizational structure Write-Host "🏗️ Discovering management group structure..." -ForegroundColor Cyan $managementGroups = Get-AzManagementGroup Write-Host "✅ Found $($managementGroups.Count) management groups" -ForegroundColor Green Write-Host "" # Initialize counters for progress tracking $totalKeyVaults = 0 $processedManagementGroups = 0 $processedSubscriptions = 0 $securityIssues = @() # Process each management group foreach ($managementGroup in $managementGroups) { $processedManagementGroups++ Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------" Write-Host "🏗️ Management Group [$($managementGroup.DisplayName)]" -ForegroundColor Cyan Write-Host " ID: $($managementGroup.Id)" -ForegroundColor DarkGray try { # Get active subscriptions in this management group $subscriptions = Get-AzManagementGroupSubscription -Group $managementGroup.Name | Where-Object State -eq "Active" Write-Host " 📋 Found $($subscriptions.Count) active subscriptions" -ForegroundColor Green foreach ($subscription in $subscriptions) { $processedSubscriptions++ Write-Host "" Write-Host " 🔄 Processing Subscription: $($subscription.DisplayName)" -ForegroundColor Yellow try { # Extract subscription ID and set context $scope = $subscription.Id.Substring($subscription.Parent.Length, $subscription.Id.Length - $subscription.Parent.Length) $subscriptionId = $scope.Replace("/subscriptions/", "") Write-Host " ID: $subscriptionId" -ForegroundColor DarkGray Set-AzContext -SubscriptionId $subscriptionId | Out-Null # Get all resource groups in the subscription $allResourceGroups = Get-AzResourceGroup [ResourceCheck[]]$Result = @() $subscriptionKeyVaults = 0 foreach ($group in $allResourceGroups) { Write-Host " 📁 Resource Group: $($group.ResourceGroupName)" -ForegroundColor DarkCyan -NoNewline try { # Get Key Vaults in this resource group $allVaults = Get-AzKeyVault -ResourceGroupName $group.ResourceGroupName -ErrorAction SilentlyContinue if ($allVaults.Count -gt 0) { Write-Host " - Found $($allVaults.Count) Key Vaults" -ForegroundColor Green $subscriptionKeyVaults += $allVaults.Count } else { Write-Host " - No Key Vaults" -ForegroundColor DarkGray } foreach ($vault in $allVaults) { try { # Get detailed Key Vault properties $vaultWithAllProps = Get-AzKeyVault -ResourceGroupName $group.ResourceGroupName -Name $vault.VaultName # Analyze security configuration $enabledRBAC = $vaultWithAllProps.EnableRbacAuthorization -eq "TRUE" # Check for security concerns if (-not $vaultWithAllProps.EnablePurgeProtection) { $securityIssues += "⚠️ Purge protection disabled: $($vaultWithAllProps.VaultName) in $($group.ResourceGroupName)" } if (-not $vaultWithAllProps.EnableSoftDelete) { $securityIssues += "⚠️ Soft delete disabled: $($vaultWithAllProps.VaultName) in $($group.ResourceGroupName)" } if ($vaultWithAllProps.PublicNetworkAccess -eq "Enabled") { $securityIssues += "🌐 Public network access enabled: $($vaultWithAllProps.VaultName) in $($group.ResourceGroupName)" } # Create resource check object [ResourceCheck] $resourceCheck = [ResourceCheck]::new() $resourceCheck.ManagementGroupId = $managementGroup.Id $resourceCheck.ManagementGroupName = $managementGroup.DisplayName $resourceCheck.ResourceId = $vaultWithAllProps.ResourceId $resourceCheck.Location = $vaultWithAllProps.Location $resourceCheck.ResourceName = $vaultWithAllProps.VaultName $resourceCheck.ResourceGroup = $vaultWithAllProps.ResourceGroupName $resourceCheck.SubscriptionId = $subscription.Id $resourceCheck.SubscriptionName = $subscription.DisplayName $resourceCheck.Tag_Team = $vaultWithAllProps.Tags.team $resourceCheck.Tag_Product = $vaultWithAllProps.Tags.product $resourceCheck.Tag_Environment = $vaultWithAllProps.Tags.environment $resourceCheck.Tag_Data = $vaultWithAllProps.Tags.data $resourceCheck.Tag_CreatedOnDate = $vaultWithAllProps.Tags.CreatedOnDate $resourceCheck.Tag_Deployment = $vaultWithAllProps.Tags.drp_deployment $resourceCheck.Prop_EnablePurgeProtection = $vaultWithAllProps.EnablePurgeProtection $resourceCheck.Prop_EnableRbacAuthorization = $enabledRBAC $resourceCheck.Prop_EnableSoftDelete = $vaultWithAllProps.EnableSoftDelete $resourceCheck.Prop_PublicNetworkAccess = $vaultWithAllProps.PublicNetworkAccess $Result += $resourceCheck $totalKeyVaults++ } catch { Write-Host " ❌ Error processing vault $($vault.VaultName): $($_.Exception.Message)" -ForegroundColor Red } } } catch { Write-Host " - ❌ Error accessing resource group: $($_.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) Key Vaults from subscription" -ForegroundColor Green } else { Write-Host " ℹ️ No Key Vaults found in subscription" -ForegroundColor DarkYellow } } catch { Write-Host " ❌ Error processing subscription: $($_.Exception.Message)" -ForegroundColor Red } } } catch { Write-Host " ❌ Error accessing management group: $($_.Exception.Message)" -ForegroundColor Red } Write-Host "" } # Calculate execution time and generate summary report $endTime = Get-Date $executionTime = $endTime - $startTime Write-Host "======================================================================================================================================================================" Write-Host "📊 AZURE KEY VAULT INVENTORY SUMMARY" -ForegroundColor Cyan Write-Host "======================================================================================================================================================================" Write-Host "⏰ Execution Time: $($executionTime.ToString('hh\:mm\:ss'))" -ForegroundColor Green Write-Host "🏗️ Management Groups Processed: $processedManagementGroups" -ForegroundColor Green Write-Host "📋 Subscriptions Processed: $processedSubscriptions" -ForegroundColor Green Write-Host "🔐 Total Key Vaults Discovered: $totalKeyVaults" -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 } # Display security analysis summary if ($securityIssues.Count -gt 0) { Write-Host "" Write-Host "🚨 SECURITY ANALYSIS SUMMARY" -ForegroundColor Red Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------" Write-Host "Found $($securityIssues.Count) potential security concerns:" -ForegroundColor Yellow foreach ($issue in $securityIssues | Select-Object -First 10) { Write-Host " $issue" -ForegroundColor Yellow } if ($securityIssues.Count -gt 10) { Write-Host " ... and $($securityIssues.Count - 10) more issues (see CSV for complete details)" -ForegroundColor DarkYellow } Write-Host "" Write-Host "📋 Recommendations:" -ForegroundColor Cyan Write-Host " • Enable purge protection on production Key Vaults" -ForegroundColor White Write-Host " • Ensure soft delete is enabled for data recovery capabilities" -ForegroundColor White Write-Host " • Consider disabling public network access where possible" -ForegroundColor White Write-Host " • Implement RBAC authorization for enhanced security" -ForegroundColor White } else { Write-Host "" Write-Host "✅ SECURITY ANALYSIS: No major security concerns detected" -ForegroundColor Green } Write-Host "" Write-Host "📈 NEXT STEPS:" -ForegroundColor Cyan Write-Host " 1. Review the generated CSV file for detailed Key Vault configurations" -ForegroundColor White Write-Host " 2. Analyze governance tags for compliance with organizational standards" -ForegroundColor White Write-Host " 3. Address any security recommendations identified above" -ForegroundColor White Write-Host " 4. Consider implementing automated monitoring for Key Vault configurations" -ForegroundColor White Write-Host "" Write-Host "✅ Azure Key Vault 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 Key Vault Reader permissions on target subscriptions" -ForegroundColor White Write-Host " 3. Check that the Management Group Reader role is assigned" -ForegroundColor White Write-Host " 4. Verify Azure PowerShell modules are installed and up to date" -ForegroundColor White Write-Host " 5. Try running the script with -Verbose for additional diagnostic information" -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" }