Private/Test-KeyVaultItemExpiration.ps1
|
function Test-KeyVaultItemExpiration { <# .SYNOPSIS Tests that all items (keys or secrets) in Key Vaults have expiration dates set. .DESCRIPTION Parameterized helper function that checks expiration dates for keys or secrets in either RBAC or non-RBAC vaults. Used by the 4 specific check functions. .PARAMETER CheckMetadata Hashtable containing check metadata (id, service, title, severity). .PARAMETER ItemType Type of item to check: 'Keys' or 'Secrets'. .PARAMETER RequireRbac If true, only check RBAC-enabled vaults. If false, only check non-RBAC vaults. .OUTPUTS [PSCustomObject[]] Array of finding objects. #> [CmdletBinding()] [OutputType([PSCustomObject[]])] param( [Parameter(Mandatory)] [hashtable]$CheckMetadata, [Parameter(Mandatory)] [ValidateSet('Keys', 'Secrets')] [string]$ItemType, [Parameter(Mandatory)] [bool]$RequireRbac ) $ErrorActionPreference = 'Stop' $rbacLabel = if ($RequireRbac) { 'RBAC' } else { 'Non-RBAC' } $itemTypeLower = $ItemType.ToLower() $itemTypeSingular = $itemTypeLower.TrimEnd('s') foreach ($subscriptionId in $script:KeyVaultService.Keys) { $kvData = $script:KeyVaultService[$subscriptionId] foreach ($vault in $kvData.KeyVaults) { $vaultName = $vault.name $location = $vault.location $resourceId = $vault.id # Strict mode safe property access $isRbacEnabled = if ($vault.properties.PSObject.Properties['enableRbacAuthorization']) { $vault.properties.enableRbacAuthorization -eq $true } else { $false } # Skip vaults that don't match the RBAC requirement if ($RequireRbac -and -not $isRbacEnabled) { continue } if (-not $RequireRbac -and $isRbacEnabled) { continue } # Get items for this vault $items = $kvData.$ItemType[$vaultName] # If we couldn't access items (permissions issue), report as manual check if ($null -eq $items) { $params = @{ CheckMetadata = $CheckMetadata Status = 'MANUAL' StatusExtended = "Cannot access $itemTypeLower in $rbacLabel vault '$vaultName' - data plane access denied. Manual verification required." ResourceId = $resourceId ResourceName = $vaultName Location = $location } New-CIEMFinding @params continue } # If no items exist, vault passes by default if ($items.Count -eq 0) { $params = @{ CheckMetadata = $CheckMetadata Status = 'PASS' StatusExtended = "$rbacLabel vault '$vaultName' has no $itemTypeLower configured." ResourceId = $resourceId ResourceName = $vaultName Location = $location } New-CIEMFinding @params continue } # Check each item for expiration (Prowler reports per-item FAIL, per-vault PASS) $hasItemWithoutExpiration = $false foreach ($item in $items) { # Keys use 'kid', secrets use 'id' for the identifier $idProperty = if ($ItemType -eq 'Keys') { 'kid' } else { 'id' } $itemName = ($item.$idProperty -split '/')[-1] # Check if item is enabled (Prowler only checks enabled items) $isEnabled = if ($item.PSObject.Properties['attributes'] -and $item.attributes.PSObject.Properties['enabled']) { $item.attributes.enabled -eq $true } else { $true # Default to enabled if property missing } # Strict mode safe: check if attributes.exp property exists (Prowler uses 'expires') $hasExpiration = $item.PSObject.Properties['attributes'] -and $item.attributes.PSObject.Properties['exp'] -and $null -ne $item.attributes.exp # Prowler: if not expires and enabled -> FAIL if (-not $hasExpiration -and $isEnabled) { $hasItemWithoutExpiration = $true $params = @{ CheckMetadata = $CheckMetadata Status = 'FAIL' StatusExtended = "Keyvault $vaultName has the $itemTypeSingular $itemName without expiration date set." ResourceId = $resourceId ResourceName = $vaultName Location = $location } New-CIEMFinding @params } } # Prowler: One PASS per vault if no items without expiration if (-not $hasItemWithoutExpiration) { $params = @{ CheckMetadata = $CheckMetadata Status = 'PASS' StatusExtended = "Keyvault $vaultName has all the $itemTypeLower with expiration date set." ResourceId = $resourceId ResourceName = $vaultName Location = $location } New-CIEMFinding @params } } } } |