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,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 "======================================================================================================================================================================"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user