Files
Cloud-20Engineering/Powershell/Lists/Azure/KeyVaultAccessPolicies.ps1
Jurjen Ladenius a226ca97ac added documetation
2025-11-03 08:12:01 +01:00

445 lines
25 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<#
.SYNOPSIS
Exports Azure Key Vault access policies across all management groups and subscriptions for security auditing and compliance.
.DESCRIPTION
This script performs a comprehensive audit of Azure Key Vault access policies across an entire
Azure tenant, scanning all management groups, subscriptions, and resource groups. It identifies
Key Vaults using legacy access policy-based authentication (not RBAC) and exports detailed
permission information for security analysis and compliance reporting.
The script is designed for:
- Security auditing and access review processes
- Compliance reporting for Key Vault permissions
- Access policy governance and standardization
- Migration planning from access policies to RBAC
- Risk assessment of Key Vault permissions
- Regular security reviews and attestation processes
Key features:
- Multi-tenant hierarchical scanning (Management Groups → Subscriptions → Key Vaults)
- Access policy-based Key Vault filtering (excludes RBAC-only vaults)
- Comprehensive permission breakdown (Keys, Secrets, Certificates, Storage)
- Identity resolution with display names and application details
- Resource tagging extraction for governance analysis
- Structured CSV export for security team analysis
The script specifically targets Key Vaults using traditional access policies and skips
those configured for RBAC-only access, providing focused analysis on legacy permission models.
.PARAMETER None
This script does not accept parameters and processes all accessible management groups and subscriptions automatically.
.EXAMPLE
.\KeyVaultAccessPolicies.ps1
Runs the complete Key Vault access policy audit across all management groups and subscriptions.
.EXAMPLE
# Connect with specific account first
Connect-AzAccount -Tenant "your-tenant-id"
.\KeyVaultAccessPolicies.ps1
Authenticates with specific tenant context before running the comprehensive audit.
.EXAMPLE
# Schedule for automated security reviews
$scriptPath = "C:\SecurityScripts\KeyVaultAccessPolicies.ps1"
& $scriptPath
Executes the script from a scheduled security review process for regular compliance reporting.
.NOTES
Author: Cloud Engineering Team
Version: 1.0
Prerequisites:
- Azure PowerShell module (Az) must be installed
- User must be authenticated to Azure (Connect-AzAccount)
- User must have at least 'Reader' permissions across all target subscriptions
- Access to Management Group hierarchy and Key Vault resources
Required Permissions:
- Management Group Reader permissions at the tenant root or target management groups
- Reader access to all subscriptions containing Key Vaults
- Key Vault Reader or Key Vault Contributor permissions on Key Vault resources
- Microsoft Graph permissions may be needed for identity display name resolution
Security Context:
- This script reads access policy configurations but does not modify them
- Exported data contains sensitive permission information - handle appropriately
- Consider running from secure, controlled environments only
- Ensure proper access controls on output files
Output File:
- Format: "YYYY-MM-DD HHMM azure_key_vault_access_policies.csv"
- Location: Current directory
- Content: Comprehensive access policy inventory with identity and permission details
CSV Structure:
- Management Group information (ID, Name)
- Subscription details (ID, Name)
- Key Vault resource information (ID, Name, Location, Resource Group)
- Access Policy details (Object ID, Display Name, Application ID, Application Name)
- Permission breakdowns (Keys, Secrets, Certificates, Storage permissions)
- Resource tags (Team, Product, Environment, Data classification, Deployment, Creation date)
Performance Considerations:
- Processing time depends on the number of management groups, subscriptions, and Key Vaults
- Large Azure tenants may require extended execution time
- Network latency affects resource enumeration and permission retrieval
- Consider running during off-peak hours for large environments
Security and Compliance Notes:
- Key Vault access policies are being deprecated in favor of RBAC
- This script helps identify vaults still using legacy access policies
- Use results to plan migration from access policies to Azure RBAC
- Regular execution recommended for continuous security monitoring
- Exported data should be classified and protected appropriately
Filtering Logic:
- Only processes Key Vaults with EnableRbacAuthorization = FALSE
- Skips RBAC-only Key Vaults (EnableRbacAuthorization = TRUE)
- Focuses analysis on legacy access policy configurations
Common Use Cases:
- Quarterly security reviews and access attestation
- Pre-migration analysis for RBAC conversion projects
- Compliance audits requiring Key Vault permission documentation
- Risk assessments of privileged access to cryptographic resources
- Governance reviews of Key Vault access patterns
.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
#>
# Ensure user is authenticated to Azure
# Uncomment the following line if authentication is needed:
# Connect-AzAccount
# Define comprehensive resource information class for Key Vault access policy analysis
class ResourceCheck {
# 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
# Access policy identity information
[string] $AccessPolicy_ObjectId = "" # Azure AD Object ID with access
[string] $AccessPolicy_DisplayName = "" # Display name of the identity (user/service principal/group)
[string] $AccessPolicy_ApplicationId = "" # Application ID (for service principals)
[string] $AccessPolicy_ApplicationDisplayName = "" # Application display name
# Permission details by Key Vault resource type
[string] $AccessPolicy_Keys = "" # Permissions granted to cryptographic keys
[string] $AccessPolicy_Secrets = "" # Permissions granted to secrets
[string] $AccessPolicy_Certificates = "" # Permissions granted to certificates
[string] $AccessPolicy_Storage = "" # Permissions granted to storage account keys
# 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 "Azure Key Vault Access Policy Security Audit"
Write-Host "======================================================================================================================================================================"
Write-Host "Starting comprehensive Key Vault access policy analysis across Azure tenant..."
Write-Host "Script execution started: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')"
Write-Host ""
Write-Host "Scope: All Management Groups → All Active Subscriptions → All Key Vaults (Access Policy-based only)"
Write-Host "Target: Key Vaults using legacy access policies (excludes RBAC-only vaults)"
Write-Host "======================================================================================================================================================================"
Write-Host ""
# Generate timestamped filename for export
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
$fileName = ".\$date azure_key_vault_access_policies.csv"
Write-Host "Export file: $fileName"
Write-Host ""
# Initialize processing counters for statistics
$totalManagementGroups = 0
$totalSubscriptions = 0
$totalKeyVaults = 0
$totalAccessPolicies = 0
$rbacOnlyVaults = 0
$processedResourceGroups = 0
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 ""
# 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++
# 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
$subscriptionAccessPolicies = 0
$subscriptionRbacVaults = 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 including access policies
$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: $($vaultWithAllProps.ResourceId)"
# Check if vault has any access policies configured
if (-not $vaultWithAllProps.AccessPolicies -or $vaultWithAllProps.AccessPolicies.Count -eq 0) {
Write-Host " ⚠ Warning: No access policies found on this vault" -ForegroundColor Yellow
continue
}
Write-Host " ✓ Found $($vaultWithAllProps.AccessPolicies.Count) access policy/policies"
# Process each access policy in the vault
foreach ($accessPolicy in $vaultWithAllProps.AccessPolicies) {
Write-Host " Identity: $($accessPolicy.DisplayName) ($($accessPolicy.ObjectId))"
# 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 access policy identity information
$resourceCheck.AccessPolicy_ObjectId = $accessPolicy.ObjectId
$resourceCheck.AccessPolicy_DisplayName = $accessPolicy.DisplayName
$resourceCheck.AccessPolicy_ApplicationId = $accessPolicy.ApplicationId
$resourceCheck.AccessPolicy_ApplicationDisplayName = $accessPolicy.ApplicationIdDisplayName
# Populate permission details for each resource type
$resourceCheck.AccessPolicy_Keys = $accessPolicy.PermissionsToKeysStr
$resourceCheck.AccessPolicy_Secrets = $accessPolicy.PermissionsToSecretsStr
$resourceCheck.AccessPolicy_Certificates = $accessPolicy.PermissionsToCertificatesStr
$resourceCheck.AccessPolicy_Storage = $accessPolicy.PermissionsToStorageStr
# 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
$totalAccessPolicies++
$subscriptionAccessPolicies++
}
} 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 " Access policies extracted: $subscriptionAccessPolicies"
Write-Host " RBAC-only vaults (skipped): $subscriptionRbacVaults"
Write-Host ""
# Export subscription results to CSV file
if ($Result.Count -gt 0) {
Write-Host "Exporting $($Result.Count) access policy 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 access policy 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 "======================================================================================================================================================================"
Write-Host "Key Vault Access Policy Audit 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 " Access policies extracted: $totalAccessPolicies"
Write-Host " RBAC-only vaults (skipped): $rbacOnlyVaults"
Write-Host ""
# Analyze and display key findings
if ($totalAccessPolicies -gt 0) {
Write-Host "✓ Successfully exported Key Vault access policy data 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: $totalAccessPolicies"
}
} else {
Write-Host " No Key Vault access policies found across all processed subscriptions"
Write-Host " This could indicate:"
Write-Host " - All Key Vaults are using RBAC-only authentication"
Write-Host " - No Key Vaults exist in the scanned subscriptions"
Write-Host " - Permission issues preventing access to Key Vault configurations"
}
Write-Host ""
Write-Host "Security Recommendations:"
Write-Host " - Review exported access policies for excessive permissions"
Write-Host " - Validate that all identities with access are still required"
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 access reviews and permission audits"
Write-Host " - Ensure proper governance tags are applied to all Key Vaults"
Write-Host ""
Write-Host "Compliance Notes:"
Write-Host " - Exported data contains sensitive security information"
Write-Host " - Handle output file with appropriate data classification controls"
Write-Host " - Consider encryption for long-term storage of audit results"
Write-Host " - Schedule regular execution for continuous security monitoring"
Write-Host "======================================================================================================================================================================"