added documetation

This commit is contained in:
Jurjen Ladenius
2025-11-03 08:12:01 +01:00
parent 8840b0e300
commit a226ca97ac
37 changed files with 8315 additions and 1481 deletions

View File

@@ -1,101 +1,565 @@
#Connect-AzAccount
<#
.SYNOPSIS
Inventories secrets in Azure Key Vaults using legacy access policies across all management groups and subscriptions.
[string] $userObjectId = "c6025a2e-416c-42da-96ef-dd507382793a" #Should be interactive user (this one is Jurjen)
.DESCRIPTION
This script performs a comprehensive inventory of secrets stored in Azure Key Vaults that use
traditional access policy-based authentication (not RBAC). It temporarily grants list permissions
to a specified user account, enumerates all secret names, and then removes the temporary access.
⚠️ SECURITY WARNING: This script temporarily modifies Key Vault access policies during execution.
It grants temporary secret list permissions to the specified user account and removes them afterwards.
The script is designed for:
- Security auditing and secret inventory management
- Compliance reporting for secret governance
- Migration planning from access policies to RBAC
- Secret lifecycle management and cleanup identification
- Risk assessment of stored secrets across the organization
- Regular security reviews and secret attestation processes
Key features:
- Multi-tenant hierarchical scanning (Management Groups → Subscriptions → Key Vaults)
- Access policy-based Key Vault filtering (excludes RBAC-only vaults)
- Temporary access policy modification for secret enumeration
- Automatic cleanup of temporary permissions
- Comprehensive secret name inventory (does not retrieve secret values)
- Resource tagging extraction for governance analysis
- Structured CSV export for security team analysis
IMPORTANT SECURITY CONSIDERATIONS:
- Script only retrieves secret names, not secret values
- Temporary access policies are automatically cleaned up
- Requires privileged permissions to modify Key Vault access policies
- Should be run from secure, controlled environments only
- All activities are logged in Azure Activity Log for audit trails
.PARAMETER None
This script does not accept command-line parameters. The user Object ID must be configured
within the script before execution.
.EXAMPLE
# Update the userObjectId variable with your Object ID first
$userObjectId = "your-user-object-id-here"
.\KeyVaultNonRBACSecrets.ps1
Runs the complete Key Vault secret inventory after configuring the user Object ID.
.EXAMPLE
# Get your current user Object ID
$currentUser = Get-AzADUser -Mail (Get-AzContext).Account.Id
Write-Host "Your Object ID: $($currentUser.Id)"
# Then update the script and run
.\KeyVaultNonRBACSecrets.ps1
Retrieves your Object ID for configuration and runs the inventory.
.EXAMPLE
# Connect with specific account first for security
Connect-AzAccount -Tenant "your-tenant-id"
.\KeyVaultNonRBACSecrets.ps1
Authenticates with specific tenant context before running the sensitive inventory operation.
.NOTES
Author: Cloud Engineering Team
Version: 1.0
⚠️ SECURITY NOTICE: This script requires and uses highly privileged permissions to temporarily
modify Key Vault access policies. Use with extreme caution and only in authorized security
audit scenarios.
Prerequisites:
- Azure PowerShell module (Az) must be installed
- User must be authenticated to Azure (Connect-AzAccount)
- User must have Key Vault Access Policy management permissions across target vaults
- User Object ID must be configured in the script before execution
Required Permissions:
- Management Group Reader permissions at the tenant root or target management groups
- Key Vault Contributor or Key Vault Access Policy Administrator on all target Key Vaults
- Reader access to all subscriptions containing Key Vaults
- Sufficient privileges to modify and remove Key Vault access policies
Security Context:
- Script temporarily grants 'List' permissions on secrets to the specified user
- Access policies are automatically removed after secret enumeration
- Only secret names are collected, not secret values
- All access policy modifications are logged in Azure Activity Log
- Failed cleanup operations may leave temporary permissions (manual removal required)
Output File:
- Format: "YYYY-MM-DD HHMM azure_key_vault_secrets.csv"
- Location: Current directory
- Content: Secret inventory with Key Vault context and governance tags
CSV Structure:
- Management Group information (ID, Name)
- Subscription details (ID, Name)
- Key Vault resource information (ID, Name, Location, Resource Group)
- Secret name (Secret_Key field)
- Resource tags (Team, Product, Environment, Data classification, Deployment, Creation date)
Performance Considerations:
- Processing time depends on the number of Key Vaults and secrets
- Access policy modifications add latency to each Key Vault operation
- Large Azure tenants may require extended execution time
- Network latency affects both enumeration and policy modification operations
Risk Mitigation:
- Script implements automatic cleanup of temporary permissions
- Only grants minimal required permissions (List secrets only)
- Does not retrieve or expose secret values
- Focuses only on access policy-based vaults (skips RBAC vaults)
- All operations are auditable through Azure Activity Log
Failure Scenarios:
- If script fails during execution, temporary access policies may remain
- Manual cleanup may be required using Remove-AzKeyVaultAccessPolicy
- Network interruptions may prevent proper cleanup
- Insufficient permissions may cause partial processing
Compliance and Governance:
- Use only for authorized security audits and compliance activities
- Document all executions for audit trail purposes
- Ensure proper approval for access policy modification activities
- Handle exported secret names with appropriate data classification
- Consider encryption for long-term storage of inventory results
.LINK
https://docs.microsoft.com/en-us/azure/key-vault/general/security-features
https://docs.microsoft.com/en-us/azure/key-vault/general/rbac-guide
https://docs.microsoft.com/en-us/azure/key-vault/general/logging
#>
# Ensure user is authenticated to Azure
# Uncomment the following line if authentication is needed:
# Connect-AzAccount
# ⚠️ SECURITY CONFIGURATION: Dynamically retrieve current user's Object ID for temporary access policy grants
# This user will receive temporary 'List' permissions on secrets during processing
Write-Host "Retrieving current user's Object ID for temporary access policy grants..."
try {
$currentContext = Get-AzContext -ErrorAction Stop
if (-not $currentContext) {
throw "No Azure context found. Please run Connect-AzAccount first."
}
# Get the current user's Object ID from Azure AD
$currentUser = Get-AzADUser -Mail $currentContext.Account.Id -ErrorAction Stop
[string] $userObjectId = $currentUser.Id
if ([string]::IsNullOrEmpty($userObjectId)) {
throw "Could not retrieve Object ID for current user: $($currentContext.Account.Id)"
}
Write-Host "✓ Successfully retrieved Object ID for user: $($currentContext.Account.Id)"
Write-Host " Object ID: $userObjectId"
Write-Host " Display Name: $($currentUser.DisplayName)"
} catch {
Write-Error "Failed to retrieve current user's Object ID: $($_.Exception.Message)"
Write-Error "Please ensure you are authenticated with Connect-AzAccount and have a valid user account"
Write-Error "Note: Service Principal authentication is not supported for this operation"
throw $_
}
# Define comprehensive resource information class for Key Vault secret inventory
class ResourceCheck {
[string] $ManagementGroupId = ""
[string] $ManagementGroupName = ""
[string] $SubscriptionId = ""
[string] $SubscriptionName = ""
[string] $ResourceGroup = ""
[string] $ResourceId = ""
[string] $Location = ""
[string] $ResourceName = ""
[string] $Secret_Key = ""
[string] $Tag_Team = ""
[string] $Tag_Product = ""
[string] $Tag_Environment = ""
[string] $Tag_Data = ""
[string] $Tag_Deployment = ""
[string] $Tag_CreatedOnDate = ""
# Management Group hierarchy information
[string] $ManagementGroupId = "" # Azure Management Group ID
[string] $ManagementGroupName = "" # Management Group display name
# Subscription context information
[string] $SubscriptionId = "" # Azure subscription ID
[string] $SubscriptionName = "" # Subscription display name
# Resource location and identification
[string] $ResourceGroup = "" # Resource group containing the Key Vault
[string] $ResourceId = "" # Full Azure resource ID of the Key Vault
[string] $Location = "" # Azure region where Key Vault is deployed
[string] $ResourceName = "" # Key Vault name
# Secret inventory information
[string] $Secret_Key = "" # Name of the secret (not the secret value)
# Resource governance tags for compliance and organization
[string] $Tag_Team = "" # Team responsible for the Key Vault
[string] $Tag_Product = "" # Product or service associated with the Key Vault
[string] $Tag_Environment = "" # Environment classification (dev/test/prod)
[string] $Tag_Data = "" # Data classification level
[string] $Tag_Deployment = "" # Deployment method or automation tag
[string] $Tag_CreatedOnDate = "" # Resource creation timestamp
}
Write-Host "======================================================================================================================================================================"
Write-Host "Creating key vault secrets overview for key vaults with access policies."
Write-Host "🔐 Azure Key Vault Secret Inventory (Access Policy-Based Vaults Only)"
Write-Host "======================================================================================================================================================================"
Write-Host "⚠️ SECURITY WARNING: This script temporarily modifies Key Vault access policies during execution"
Write-Host "Script execution started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host ""
Write-Host "Security Configuration:"
Write-Host " User Object ID for temporary access: $userObjectId"
Write-Host " Permissions granted: List secrets only (temporary)"
Write-Host " Scope: Access policy-based Key Vaults only (RBAC vaults excluded)"
Write-Host " Data collected: Secret names only (values are NOT retrieved)"
Write-Host "======================================================================================================================================================================"
Write-Host ""
# Validate that we successfully retrieved a user Object ID
if ([string]::IsNullOrEmpty($userObjectId)) {
Write-Host "❌ CRITICAL ERROR: Could not retrieve current user's Object ID" -ForegroundColor Red
Write-Host ""
Write-Host "This could indicate:"
Write-Host " - You are not authenticated to Azure (run Connect-AzAccount)"
Write-Host " - You are authenticated with a Service Principal (user account required)"
Write-Host " - Your account is not found in Azure AD"
Write-Host " - Insufficient permissions to query Azure AD user information"
Write-Host ""
Write-Host "Please ensure you are authenticated with a valid user account and try again."
Write-Host "======================================================================================================================================================================"
throw "User Object ID retrieval failed"
}
# Generate timestamped filename for export
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
$fileName = ".\$date azure_key_vault_secrets.csv"
$fileName = ".\$date azure_key_vault_secrets.csv"
Write-Host "Export file: $fileName"
Write-Host ""
$managementGroups = Get-AzManagementGroup
# Initialize processing counters and security tracking
$totalManagementGroups = 0
$totalSubscriptions = 0
$totalKeyVaults = 0
$totalSecrets = 0
$rbacOnlyVaults = 0
$processedResourceGroups = 0
$accessPolicyModifications = 0
$cleanupFailures = @()
foreach ($managementGroup in $managementGroups)
{
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
Write-Host "Management group [$($managementGroup.Name)]"
Write-Host "Discovering Management Group hierarchy..."
try {
$managementGroups = Get-AzManagementGroup -ErrorAction Stop
$totalManagementGroups = $managementGroups.Count
Write-Host "✓ Found $totalManagementGroups Management Group(s) to process:"
foreach ($mg in $managementGroups) {
Write-Host " - $($mg.DisplayName) ($($mg.Name))"
}
} catch {
Write-Error "Failed to retrieve Management Groups. Please ensure you have appropriate permissions."
Write-Error "Required permissions: Management Group Reader at tenant root or target management groups"
throw $_
}
Write-Host ""
$subscriptions = Get-AzManagementGroupSubscription -Group $managementGroup.Name | Where-Object State -eq "Active"
foreach ($subscription in $subscriptions)
{
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
$scope = $subscription.Id.Substring($subscription.Parent.Length, $subscription.Id.Length - $subscription.Parent.Length)
$subscriptionId = $scope.Replace("/subscriptions/", "")
Write-Host "Subscription [$($subscription.DisplayName) - $subscriptionId]"
Set-AzContext -SubscriptionId $subscriptionId | Out-Null
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
$allResourceGroups = Get-AzResourceGroup
[ResourceCheck[]]$Result = @()
foreach ($group in $allResourceGroups) {
$allVaults = Get-AzKeyVault -ResourceGroupName $group.ResourceGroupName
foreach ($vault in $allVaults) {
Write-Host $vault.VaultName
$vaultWithAllProps = Get-AzKeyVault -ResourceGroupName $group.ResourceGroupName -Name $vault.VaultName
if ($vaultWithAllProps.EnableRbacAuthorization -ne "TRUE") {
Write-Host " -- processing..."
Set-AzKeyVaultAccessPolicy -VaultName $vault.VaultName -ObjectId $userObjectId -PermissionsToSecrets "List"
$secrets = Get-AzKeyVaultSecret -VaultName $vault.VaultName
foreach($secret in $secrets)
{
[ResourceCheck] $resourceCheck = [ResourceCheck]::new()
$resourceCheck.ManagementGroupId = $managementGroup.Id
$resourceCheck.ManagementGroupName = $managementGroup.DisplayName
$resourceCheck.SubscriptionId = $subscription.Id
$resourceCheck.SubscriptionName = $subscription.Name
$resourceCheck.ResourceGroup = $vaultWithAllProps.ResourceGroupName
$resourceCheck.ResourceId = $vaultWithAllProps.ResourceId
$resourceCheck.Location = $vaultWithAllProps.Location
$resourceCheck.ResourceName = $vaultWithAllProps.VaultName
$resourceCheck.Secret_Key = $secret.Name
$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
$Result += $resourceCheck
}
Remove-AzKeyVaultAccessPolicy -VaultName $vault.VaultName -ObjectId $userObjectId
}
}
# Process each Management Group in the hierarchy
foreach ($managementGroup in $managementGroups) {
Write-Host "======================================================================================================================================================================"
Write-Host "Processing Management Group: $($managementGroup.DisplayName) ($($managementGroup.Name))"
Write-Host "======================================================================================================================================================================"
try {
# Get all active subscriptions within this management group
Write-Host "Discovering active subscriptions in management group..."
$subscriptions = Get-AzManagementGroupSubscription -Group $managementGroup.Name -ErrorAction Stop | Where-Object State -eq "Active"
if (-not $subscriptions -or $subscriptions.Count -eq 0) {
Write-Host " No active subscriptions found in management group '$($managementGroup.DisplayName)'"
continue
}
Write-Host "✓ Found $($subscriptions.Count) active subscription(s):"
foreach ($sub in $subscriptions) {
Write-Host " - $($sub.DisplayName)"
}
Write-Host ""
# Process each active subscription
foreach ($subscription in $subscriptions) {
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
# Extract subscription ID from the full resource path
$scope = $subscription.Id.Substring($subscription.Parent.Length, $subscription.Id.Length - $subscription.Parent.Length)
$subscriptionId = $scope.Replace("/subscriptions/", "")
Write-Host "Processing Subscription: $($subscription.DisplayName) ($subscriptionId)"
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
try {
# Set Azure context to the current subscription
Set-AzContext -SubscriptionId $subscriptionId -ErrorAction Stop | Out-Null
Write-Host "✓ Successfully connected to subscription context"
$totalSubscriptions++
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
# Get all resource groups in the current subscription
Write-Host "Discovering resource groups..."
$allResourceGroups = Get-AzResourceGroup -ErrorAction Stop
Write-Host "✓ Found $($allResourceGroups.Count) resource group(s) to scan"
# Initialize result collection for this subscription
[ResourceCheck[]]$Result = @()
$subscriptionKeyVaults = 0
$subscriptionSecrets = 0
$subscriptionRbacVaults = 0
$subscriptionAccessPolicyMods = 0
# Process each resource group to find Key Vaults
foreach ($group in $allResourceGroups) {
Write-Host " Scanning resource group: $($group.ResourceGroupName)"
$processedResourceGroups++
try {
# Get all Key Vaults in the current resource group
$allVaults = Get-AzKeyVault -ResourceGroupName $group.ResourceGroupName -ErrorAction Stop
if (-not $allVaults -or $allVaults.Count -eq 0) {
Write-Host " No Key Vaults found in resource group"
continue
}
Write-Host " ✓ Found $($allVaults.Count) Key Vault(s)"
# Process each Key Vault found
foreach ($vault in $allVaults) {
Write-Host " 🔐 Processing Key Vault: $($vault.VaultName)"
try {
# Get detailed Key Vault properties
$vaultWithAllProps = Get-AzKeyVault -ResourceGroupName $group.ResourceGroupName -Name $vault.VaultName -ErrorAction Stop
$totalKeyVaults++
$subscriptionKeyVaults++
# Check if vault uses traditional access policies (not RBAC-only)
if ($vaultWithAllProps.EnableRbacAuthorization -ne $true) {
Write-Host " 📋 Access Policy-based vault - processing secrets..."
# ⚠️ SECURITY CRITICAL: Temporarily grant List permissions to enumerate secrets
try {
Write-Host " 🔑 Granting temporary List permissions to user: $userObjectId"
Set-AzKeyVaultAccessPolicy -VaultName $vault.VaultName -ObjectId $userObjectId -PermissionsToSecrets "List" -ErrorAction Stop
$accessPolicyModifications++
$subscriptionAccessPolicyMods++
# Enumerate all secrets in the vault
Write-Host " 📝 Enumerating secrets..."
$secrets = Get-AzKeyVaultSecret -VaultName $vault.VaultName -ErrorAction Stop
if (-not $secrets -or $secrets.Count -eq 0) {
Write-Host " No secrets found in this vault"
} else {
Write-Host " ✓ Found $($secrets.Count) secret(s)"
# Process each secret found
foreach ($secret in $secrets) {
Write-Host " Secret: $($secret.Name)"
# Create comprehensive resource check entry
[ResourceCheck] $resourceCheck = [ResourceCheck]::new()
# Populate management group and subscription information
$resourceCheck.ManagementGroupId = $managementGroup.Id
$resourceCheck.ManagementGroupName = $managementGroup.DisplayName
$resourceCheck.SubscriptionId = $subscription.Id
$resourceCheck.SubscriptionName = $subscription.DisplayName
# Populate Key Vault resource information
$resourceCheck.ResourceGroup = $vaultWithAllProps.ResourceGroupName
$resourceCheck.ResourceId = $vaultWithAllProps.ResourceId
$resourceCheck.Location = $vaultWithAllProps.Location
$resourceCheck.ResourceName = $vaultWithAllProps.VaultName
# Populate secret information (name only, not value)
$resourceCheck.Secret_Key = $secret.Name
# Extract resource tags for governance analysis
$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
# Add to results collection
$Result += $resourceCheck
$totalSecrets++
$subscriptionSecrets++
}
}
# ⚠️ SECURITY CRITICAL: Remove temporary permissions immediately
Write-Host " 🧹 Removing temporary permissions..."
try {
Remove-AzKeyVaultAccessPolicy -VaultName $vault.VaultName -ObjectId $userObjectId -ErrorAction Stop
Write-Host " ✓ Successfully removed temporary permissions"
} catch {
Write-Host " ❌ CLEANUP FAILURE: Could not remove temporary permissions!" -ForegroundColor Red
$cleanupFailures += @{
VaultName = $vault.VaultName
ResourceGroup = $group.ResourceGroupName
SubscriptionId = $subscriptionId
Error = $_.Exception.Message
}
Write-Host " ⚠️ Manual cleanup required: Remove-AzKeyVaultAccessPolicy -VaultName '$($vault.VaultName)' -ObjectId '$userObjectId'" -ForegroundColor Yellow
}
} catch {
Write-Host " ❌ Error accessing vault secrets: $($_.Exception.Message)" -ForegroundColor Red
Write-Host " This may indicate insufficient permissions or vault access restrictions"
# Ensure cleanup attempt even on failure
try {
Remove-AzKeyVaultAccessPolicy -VaultName $vault.VaultName -ObjectId $userObjectId -ErrorAction SilentlyContinue
} catch {
# Silent cleanup attempt
}
}
} else {
Write-Host " 🔐 RBAC-only vault (skipped): $($vault.VaultName)" -ForegroundColor Blue
$rbacOnlyVaults++
$subscriptionRbacVaults++
}
} catch {
Write-Host " ❌ Error processing vault '$($vault.VaultName)': $($_.Exception.Message)" -ForegroundColor Red
}
}
} catch {
Write-Host " ❌ Error scanning resource group '$($group.ResourceGroupName)': $($_.Exception.Message)" -ForegroundColor Red
}
}
Write-Host ""
Write-Host "Subscription Summary:"
Write-Host " Key Vaults found: $subscriptionKeyVaults"
Write-Host " Secrets inventoried: $subscriptionSecrets"
Write-Host " Access policy modifications: $subscriptionAccessPolicyMods"
Write-Host " RBAC-only vaults (skipped): $subscriptionRbacVaults"
if ($cleanupFailures.Count -gt 0) {
Write-Host " ⚠️ Cleanup failures: $($cleanupFailures.Count)" -ForegroundColor Yellow
}
Write-Host ""
# Export subscription results to CSV file
if ($Result.Count -gt 0) {
Write-Host "Exporting $($Result.Count) secret entries to CSV..."
try {
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation -ErrorAction Stop
Write-Host "✓ Successfully exported subscription data"
} catch {
Write-Error "Failed to export data for subscription '$($subscription.DisplayName)': $($_.Exception.Message)"
}
} else {
Write-Host " No secret data to export from this subscription"
}
} catch {
Write-Host "❌ Error processing subscription '$($subscription.DisplayName)': $($_.Exception.Message)" -ForegroundColor Red
Write-Host " Please verify subscription access and permissions" -ForegroundColor Red
}
Write-Host ""
}
} catch {
Write-Host "❌ Error processing management group '$($managementGroup.DisplayName)': $($_.Exception.Message)" -ForegroundColor Red
Write-Host " Please verify management group access and permissions" -ForegroundColor Red
}
}
Write-Host "======================================================================================================================================================================"
Write-Host "Done."
Write-Host ""
Write-Host "======================================================================================================================================================================"
Write-Host "🔐 Key Vault Secret Inventory Completed"
Write-Host "======================================================================================================================================================================"
Write-Host "Execution completed: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host ""
# Display comprehensive execution statistics
Write-Host "Processing Statistics:"
Write-Host " Management Groups processed: $totalManagementGroups"
Write-Host " Subscriptions processed: $totalSubscriptions"
Write-Host " Resource Groups scanned: $processedResourceGroups"
Write-Host " Total Key Vaults found: $totalKeyVaults"
Write-Host " Secrets inventoried: $totalSecrets"
Write-Host " Access policy modifications: $accessPolicyModifications"
Write-Host " RBAC-only vaults (skipped): $rbacOnlyVaults"
Write-Host ""
# Critical security status check
if ($cleanupFailures.Count -gt 0) {
Write-Host "🚨 CRITICAL SECURITY ALERT: MANUAL CLEANUP REQUIRED" -ForegroundColor Red
Write-Host "======================================================================================================================================================================"
Write-Host "The following Key Vaults still have temporary permissions that need manual removal:" -ForegroundColor Red
Write-Host ""
foreach ($failure in $cleanupFailures) {
Write-Host " Vault: $($failure.VaultName)" -ForegroundColor Red
Write-Host " Resource Group: $($failure.ResourceGroup)" -ForegroundColor Red
Write-Host " Subscription: $($failure.SubscriptionId)" -ForegroundColor Red
Write-Host " Cleanup Command: Remove-AzKeyVaultAccessPolicy -VaultName '$($failure.VaultName)' -ObjectId '$userObjectId'" -ForegroundColor Yellow
Write-Host " Error: $($failure.Error)" -ForegroundColor Red
Write-Host ""
}
Write-Host "⚠️ Please run the cleanup commands above to remove temporary permissions immediately!" -ForegroundColor Yellow
Write-Host "======================================================================================================================================================================"
} else {
Write-Host "✅ Security Status: All temporary access policies were successfully removed"
}
Write-Host ""
# Analyze and display key findings
if ($totalSecrets -gt 0) {
Write-Host "✓ Successfully exported Key Vault secret inventory to: $fileName"
Write-Host ""
Write-Host "Security Analysis Insights:"
if ($rbacOnlyVaults -gt 0) {
Write-Host "$rbacOnlyVaults Key Vault(s) using modern RBAC authentication"
}
$legacyVaults = $totalKeyVaults - $rbacOnlyVaults
if ($legacyVaults -gt 0) {
Write-Host "$legacyVaults Key Vault(s) still using legacy access policies" -ForegroundColor Yellow
Write-Host " Consider migrating to Azure RBAC for improved security and management"
}
Write-Host ""
Write-Host "Output File Information:"
if (Test-Path $fileName) {
Write-Host " File Path: $fileName"
Write-Host " File Size: $([Math]::Round((Get-Item $fileName).Length / 1KB, 2)) KB"
Write-Host " Records Exported: $totalSecrets"
}
} else {
Write-Host " No Key Vault secrets found across all processed subscriptions"
Write-Host " This could indicate:"
Write-Host " - All Key Vaults are using RBAC-only authentication"
Write-Host " - No secrets exist in the scanned Key Vaults"
Write-Host " - Permission issues preventing access to Key Vault contents"
}
Write-Host ""
Write-Host "Security Recommendations:"
Write-Host " - Review exported secret inventory for unused or expired secrets"
Write-Host " - Implement secret rotation policies for all identified secrets"
Write-Host " - Consider implementing Azure RBAC for new Key Vaults"
Write-Host " - Plan migration from access policies to RBAC for existing vaults"
Write-Host " - Implement regular secret lifecycle management and cleanup"
Write-Host " - Ensure proper governance tags are applied to all Key Vaults"
Write-Host ""
Write-Host "Compliance and Security Notes:"
Write-Host " - This inventory contains sensitive secret name information"
Write-Host " - Handle output file with appropriate data classification controls"
Write-Host " - All access policy modifications are logged in Azure Activity Log"
Write-Host " - Consider encryption for long-term storage of inventory results"
Write-Host " - Schedule regular execution for continuous secret governance"
Write-Host " - Ensure manual cleanup is performed if cleanup failures occurred"
Write-Host ""
Write-Host "Audit Trail:"
Write-Host " - All temporary access policy changes are logged in Azure Activity Log"
Write-Host " - Search for 'Microsoft.KeyVault/vaults/accessPolicies/write' operations"
Write-Host " - Filter by Object ID: $userObjectId"
Write-Host " - Execution timeframe: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host "======================================================================================================================================================================"