Private/ValidationHelper.ps1
|
<# .SYNOPSIS Validation and assertion functions for parameter and file validation. .DESCRIPTION This file contains validation functions used to verify inputs, file paths, and configurations before processing. Provides consistent validation and error messaging across the module. .NOTES Internal module file - not exported to users. Functions are available to all module cmdlets. #> function Test-ConfigurationFile { <# .SYNOPSIS Validates a CSV configuration file. .DESCRIPTION Checks if a configuration file exists, is readable, has valid CSV format, and contains the required columns for DLP policy updates. .PARAMETER FilePath The path to the CSV configuration file to validate. .PARAMETER RequiredColumns Optional. Array of required column names. Default: @('PolicyName', 'RuleName', 'PolicyTipFile', 'EmailBodyFile', 'NotifyUser') .OUTPUTS System.Collections.Hashtable Returns hashtable with keys: - IsValid (bool): Whether file is valid - ErrorMessage (string): Error message if not valid - RowCount (int): Number of data rows if valid .EXAMPLE $validation = Test-ConfigurationFile -FilePath ".\config.csv" if (-not $validation.IsValid) { Write-Error $validation.ErrorMessage } #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory = $true, Position = 0)] [string]$FilePath, [Parameter(Mandatory = $false)] [string[]]$RequiredColumns = @('PolicyName', 'RuleName', 'PolicyTipFile', 'EmailBodyFile', 'NotifyUser') ) $result = @{ IsValid = $false ErrorMessage = "" RowCount = 0 } # Check file exists if (-not (Test-Path -Path $FilePath)) { $result.ErrorMessage = "Configuration file not found: $FilePath" return $result } # Check file is not empty $fileInfo = Get-Item -Path $FilePath if ($fileInfo.Length -eq 0) { $result.ErrorMessage = "Configuration file is empty: $FilePath" return $result } try { # Try to read and parse CSV $rawContent = Get-Content $FilePath -Encoding UTF8 -ErrorAction Stop $filteredContent = $rawContent | Where-Object { $_ -notmatch '^\s*#' -and $_ -notmatch '^\s*$' } if ($filteredContent.Count -eq 0) { $result.ErrorMessage = "Configuration file contains no valid data (only comments/empty lines)" return $result } $config = $filteredContent | ConvertFrom-Csv -ErrorAction Stop if ($null -eq $config -or $config.Count -eq 0) { $result.ErrorMessage = "Configuration file contains no valid CSV data" return $result } # Validate required columns $firstRow = $config[0] $actualColumns = $firstRow.PSObject.Properties.Name $missingColumns = $RequiredColumns | Where-Object { $_ -notin $actualColumns } if ($missingColumns) { $result.ErrorMessage = "Configuration file missing required columns: $($missingColumns -join ', ')" return $result } # All checks passed $result.IsValid = $true $result.RowCount = $config.Count Write-Verbose "Configuration file valid: $($config.Count) rows, columns: $($actualColumns -join ', ')" } catch { $result.ErrorMessage = "Error parsing configuration file: $($_.Exception.Message)" return $result } return $result } function Test-TemplateFile { <# .SYNOPSIS Validates a template file exists and is readable. .DESCRIPTION Checks if a template file (HTML, text, etc.) exists within the module's Templates directory and is readable. Validates file size is reasonable. .PARAMETER TemplatePath The full or relative path to the template file. .PARAMETER MaxSizeKB Optional. Maximum allowed file size in KB. Default: 1024 (1 MB) .OUTPUTS System.Collections.Hashtable Returns hashtable with keys: - IsValid (bool): Whether file is valid - ErrorMessage (string): Error message if not valid - FilePath (string): Full resolved path if valid - SizeKB (int): File size in KB if valid .EXAMPLE $validation = Test-TemplateFile -TemplatePath "Templates\PolicyTips\sim-tip.html" if ($validation.IsValid) { $content = Get-Content $validation.FilePath -Raw } #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory = $true, Position = 0)] [string]$TemplatePath, [Parameter(Mandatory = $false)] [int]$MaxSizeKB = 1024 ) $result = @{ IsValid = $false ErrorMessage = "" FilePath = "" SizeKB = 0 } # Resolve path (might be relative to module) $resolvedPath = $TemplatePath if (-not [System.IO.Path]::IsPathRooted($TemplatePath)) { $moduleRoot = Split-Path -Parent $PSScriptRoot $resolvedPath = Join-Path $moduleRoot $TemplatePath } # Check file exists if (-not (Test-Path -Path $resolvedPath)) { $result.ErrorMessage = "Template file not found: $resolvedPath" return $result } # Check it's a file not directory $item = Get-Item -Path $resolvedPath if ($item.PSIsContainer) { $result.ErrorMessage = "Path is a directory, not a file: $resolvedPath" return $result } # Check file size $sizeKB = [math]::Round($item.Length / 1KB, 2) if ($sizeKB -gt $MaxSizeKB) { $result.ErrorMessage = "Template file too large: ${sizeKB}KB (max: ${MaxSizeKB}KB)" return $result } # Try to read file try { $null = Get-Content -Path $resolvedPath -Raw -ErrorAction Stop # All checks passed $result.IsValid = $true $result.FilePath = $resolvedPath $result.SizeKB = $sizeKB Write-Verbose "Template file valid: $resolvedPath (${sizeKB}KB)" } catch { $result.ErrorMessage = "Cannot read template file: $($_.Exception.Message)" return $result } return $result } function Test-BackupFile { <# .SYNOPSIS Validates a DLP policy backup JSON file. .DESCRIPTION Checks if a backup file exists, is valid JSON, and contains expected DLP policy structure with policies and rules. .PARAMETER FilePath The path to the backup JSON file. .OUTPUTS System.Collections.Hashtable Returns hashtable with keys: - IsValid (bool): Whether file is valid - ErrorMessage (string): Error message if not valid - PolicyCount (int): Number of policies if valid - RuleCount (int): Total number of rules if valid - CreatedDate (datetime): File creation date if valid .EXAMPLE $validation = Test-BackupFile -FilePath ".\backup.json" if ($validation.IsValid) { Write-Host "Found $($validation.PolicyCount) policies" } #> [CmdletBinding()] [OutputType([hashtable])] param( [Parameter(Mandatory = $true, Position = 0)] [string]$FilePath ) $result = @{ IsValid = $false ErrorMessage = "" PolicyCount = 0 RuleCount = 0 CreatedDate = $null } # Check file exists if (-not (Test-Path -Path $FilePath)) { $result.ErrorMessage = "Backup file not found: $FilePath" return $result } # Check file is not empty $fileInfo = Get-Item -Path $FilePath if ($fileInfo.Length -eq 0) { $result.ErrorMessage = "Backup file is empty: $FilePath" return $result } $result.CreatedDate = $fileInfo.CreationTime try { # Try to parse JSON $content = Get-Content -Path $FilePath -Raw -Encoding UTF8 -ErrorAction Stop $backup = $content | ConvertFrom-Json -ErrorAction Stop # Validate structure if (-not $backup) { $result.ErrorMessage = "Backup file contains no data" return $result } # Check for policies array if (-not $backup.Policies) { $result.ErrorMessage = "Backup file missing 'Policies' property" return $result } if ($backup.Policies -isnot [array]) { $result.ErrorMessage = "Backup file 'Policies' property is not an array" return $result } # Count policies and rules $result.PolicyCount = $backup.Policies.Count $result.RuleCount = ($backup.Policies | ForEach-Object { if ($_.Rules) { $_.Rules.Count } else { 0 } } | Measure-Object -Sum).Sum # All checks passed $result.IsValid = $true Write-Verbose "Backup file valid: $($result.PolicyCount) policies, $($result.RuleCount) rules" } catch { $result.ErrorMessage = "Error parsing backup file: $($_.Exception.Message)" return $result } return $result } function Assert-RequiredParameter { <# .SYNOPSIS Asserts that a required parameter has a value. .DESCRIPTION Validates that a parameter value is not null, empty, or whitespace. Throws a descriptive error if validation fails. Useful for runtime parameter validation in cmdlets. .PARAMETER Value The parameter value to check. .PARAMETER ParameterName The name of the parameter (for error messages). .PARAMETER AllowZero If specified, allows numeric zero values (default behavior rejects them). .EXAMPLE Assert-RequiredParameter -Value $PolicyName -ParameterName "PolicyName" # Throws if PolicyName is null/empty .EXAMPLE Assert-RequiredParameter -Value $Count -ParameterName "Count" -AllowZero # Allows 0 as a valid value .NOTES This function throws a terminating error if validation fails. #> [CmdletBinding()] param( [Parameter(Mandatory = $false, Position = 0)] [object]$Value, [Parameter(Mandatory = $true, Position = 1)] [string]$ParameterName, [Parameter(Mandatory = $false)] [switch]$AllowZero ) $isInvalid = $false $errorMessage = "" if ($null -eq $Value) { $isInvalid = $true $errorMessage = "Parameter '$ParameterName' cannot be null" } elseif ($Value -is [string] -and [string]::IsNullOrWhiteSpace($Value)) { $isInvalid = $true $errorMessage = "Parameter '$ParameterName' cannot be empty or whitespace" } elseif ($Value -is [array] -and $Value.Count -eq 0) { $isInvalid = $true $errorMessage = "Parameter '$ParameterName' cannot be an empty array" } elseif (-not $AllowZero -and $Value -is [int] -and $Value -eq 0) { $isInvalid = $true $errorMessage = "Parameter '$ParameterName' cannot be zero (use -AllowZero if zero is valid)" } if ($isInvalid) { Write-ColorOutput $errorMessage -Type Error throw $errorMessage } Write-Verbose "Parameter '$ParameterName' validation passed" } function Test-DLPConnection { <# .SYNOPSIS Tests and validates connection to Security & Compliance Center. .DESCRIPTION Checks if there is an active connection to Security & Compliance Center and throws a descriptive error with remediation steps if not connected. This is a convenience wrapper around Test-SecurityComplianceConnection that provides better error messages. .EXAMPLE Test-DLPConnection # Throws if not connected, otherwise returns silently .NOTES This function throws a terminating error if validation fails. Use at the beginning of cmdlets that require an active connection. #> [CmdletBinding()] param() if (-not (Test-SecurityComplianceConnection)) { $errorMessage = @" Not connected to Security & Compliance Center. Please connect first using: Connect-PurviewDLP Or if using ExchangeOnlineManagement directly: Connect-IPPSSession "@ Write-ColorOutput "ERROR: Not connected to Security & Compliance Center" -Type Error Write-Host "" Write-ColorOutput "Please connect first using: Connect-PurviewDLP" -Type Warning Write-Host "" throw $errorMessage } Write-Verbose "Security & Compliance Center connection validated" } |