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