functions/System/Hardening/Export-HardeningReport.ps1
|
function Export-HardeningReport { <# .SYNOPSIS Exports hardening compliance reports in multiple formats. .DESCRIPTION Generates comprehensive hardening reports from compliance verification results. Supports multiple export formats for different use cases: - JSON: Structured data for programmatic analysis - CSV: Tabular format for Excel/spreadsheets - HTML: Formatted report for documentation/dashboards - Text: Human-readable summary report Reports include: - Executive summary with compliance metrics - Category-level breakdown - Per-rule compliance details - Remediation recommendations - Trending data (if historical reports available) .PARAMETER ComplianceReport The compliance report object from Test-HardeningCompliance. Mandatory. .PARAMETER Format Export format: JSON, CSV, HTML, or Text. Default: Text (console output) .PARAMETER OutputPath File path for exported report. If omitted with file format, outputs to console. .PARAMETER IncludeRuleDetails If specified, includes per-rule details in report. Useful for detailed compliance documentation. .PARAMETER IncludeTrending If specified, includes compliance trending data. Requires historical reports for comparison. .PARAMETER HistoricalReports Array of previous compliance reports for trending analysis. Optional. Used with -IncludeTrending. .EXAMPLE $compliance = Test-HardeningCompliance -Session $session Export-HardeningReport -ComplianceReport $compliance -Format HTML -OutputPath report.html Exports detailed HTML compliance report. .EXAMPLE $compliance = Test-HardeningCompliance -Session $session Export-HardeningReport -ComplianceReport $compliance -Format JSON | ConvertFrom-Json Exports JSON report for programmatic processing. .EXAMPLE $compliance = Test-HardeningCompliance -Session $session Export-HardeningReport -ComplianceReport $compliance -Format CSV -OutputPath compliance.csv -IncludeRuleDetails Exports CSV with detailed per-rule compliance data. .NOTES DEPENDENCIES: Write-Log (Core) OUTPUT FORMATS: JSON, CSV, HTML, Text FILE OUTPUT: Supports UTF-8 encoding with BOM #> [CmdletBinding(SupportsShouldProcess = $true)] param( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSCustomObject] $ComplianceReport, [Parameter(Mandatory = $false)] [ValidateSet('JSON', 'CSV', 'HTML', 'Text')] [string] $Format = 'Text', [Parameter(Mandatory = $false)] [string] $OutputPath, [switch] $IncludeRuleDetails, [switch] $IncludeTrending, [Parameter(Mandatory = $false)] [PSCustomObject[]] $HistoricalReports ) begin { $ErrorActionPreference = 'Stop' } process { try { Write-Log -Message "Generating $Format hardening report" -Level Info # Generate report content based on format $reportContent = switch ($Format) { 'JSON' { _GenerateJsonReport -Report $ComplianceReport -IncludeRuleDetails:$IncludeRuleDetails } 'CSV' { _GenerateCsvReport -Report $ComplianceReport -IncludeRuleDetails:$IncludeRuleDetails } 'HTML' { _GenerateHtmlReport -Report $ComplianceReport ` -IncludeRuleDetails:$IncludeRuleDetails ` -IncludeTrending:$IncludeTrending ` -HistoricalReports $HistoricalReports } 'Text' { _GenerateTextReport -Report $ComplianceReport ` -IncludeRuleDetails:$IncludeRuleDetails ` -IncludeTrending:$IncludeTrending ` -HistoricalReports $HistoricalReports } } # Output or save report if ($OutputPath) { if ($PSCmdlet.ShouldProcess($OutputPath, "Export hardening report")) { $reportContent | Out-File -FilePath $OutputPath -Encoding UTF8 -Force Write-Log -Message "Report exported to: $OutputPath" -Level Info Get-Item -Path $OutputPath } } else { $reportContent } } catch { $errMsg = "Failed to export hardening report: $($_.Exception.Message)" Write-ErrorLog -Message $errMsg -Caller $MyInvocation.MyCommand.Name throw } } } # ================================================================================ # Private Report Generation Functions # ================================================================================ function _GenerateJsonReport { <# .SYNOPSIS Internal helper: Generates JSON compliance report from verification results. #> param( [PSCustomObject]$Report, [bool]$IncludeRuleDetails ) $jsonReport = [ordered]@{ ReportMetadata = @{ GeneratedTime = $Report.VerificationTime Profile = $Report.Profile TargetSystem = $Report.TargetSystem SessionId = $Report.SessionId } ComplianceSummary = @{ TotalRules = $Report.TotalRules CompliantRules = $Report.CompliantRules NonCompliantRules = $Report.NonCompliantRules CompliancePercentage = $Report.CompliancePercentage Status = $Report.Status } CategoryBreakdown = $Report.CategoryBreakdown } if ($IncludeRuleDetails) { $jsonReport['RuleDetails'] = @($Report.RuleResults | ForEach-Object { [ordered]@{ RuleName = $_.RuleName Category = $_.Category Severity = $_.Severity Compliant = $_.Compliant ExpectedValue = $_.ExpectedValue ActualValue = $_.ActualValue } }) } $jsonReport | ConvertTo-Json -Depth 10 } function _GenerateCsvReport { <# .SYNOPSIS Internal helper: Generates CSV compliance report for spreadsheet analysis. #> param( [PSCustomObject]$Report, [bool]$IncludeRuleDetails ) if ($IncludeRuleDetails) { $csvData = @($Report.RuleResults | ForEach-Object { [PSCustomObject]@{ RuleName = $_.RuleName Category = $_.Category Severity = $_.Severity Compliant = $_.Compliant ExpectedValue = $_.ExpectedValue ActualValue = $_.ActualValue Profile = $Report.Profile TargetSystem = $Report.TargetSystem } }) } else { $csvData = [PSCustomObject]@{ Profile = $Report.Profile TargetSystem = $Report.TargetSystem TotalRules = $Report.TotalRules CompliantRules = $Report.CompliantRules NonCompliantRules = $Report.NonCompliantRules CompliancePercentage = $Report.CompliancePercentage Status = $Report.Status GeneratedTime = $Report.VerificationTime } } $csvData | ConvertTo-Csv -NoTypeInformation } function _GenerateHtmlReport { <# .SYNOPSIS Internal helper: Generates formatted HTML compliance report for dashboards and documentation. #> param( [PSCustomObject]$Report, [bool]$IncludeRuleDetails, [bool]$IncludeTrending, [PSCustomObject[]]$HistoricalReports ) $statusColor = switch ($Report.Status) { 'Fully Compliant' { '#28a745' } 'Highly Compliant' { '#17a2b8' } 'Mostly Compliant' { '#ffc107' } 'Partially Compliant' { '#fd7e14' } default { '#dc3545' } } $html = @" <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>WinHarden Hardening Compliance Report</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin: 20px; background: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } h1 { color: #333; border-bottom: 3px solid $statusColor; padding-bottom: 10px; } h2 { color: #555; margin-top: 30px; } .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin: 20px 0; } .metric { background: #f9f9f9; padding: 15px; border-left: 4px solid $statusColor; border-radius: 4px; } .metric-label { font-size: 0.9em; color: #666; } .metric-value { font-size: 1.8em; font-weight: bold; color: $statusColor; } .status-badge { display: inline-block; padding: 8px 16px; background: $statusColor; color: white; border-radius: 4px; font-weight: bold; } table { width: 100%; border-collapse: collapse; margin: 15px 0; } th { background: #f0f0f0; padding: 12px; text-align: left; font-weight: 600; border-bottom: 2px solid #ddd; } td { padding: 10px 12px; border-bottom: 1px solid #eee; } tr:hover { background: #f9f9f9; } .compliant { color: #28a745; font-weight: 500; } .non-compliant { color: #dc3545; font-weight: 500; } .category-row { background: #f5f5f5; font-weight: 600; } .footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #eee; color: #666; font-size: 0.9em; } </style> </head> <body> <div class="container"> <h1>WinHarden Hardening Compliance Report</h1> <div style="margin: 15px 0;"> <strong>Report Generated:</strong> $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') </div> <div style="margin: 15px 0;"> <strong>Status:</strong> <span class="status-badge">$($Report.Status)</span> </div> <h2>Executive Summary</h2> <div class="summary"> <div class="metric"> <div class="metric-label">Total Rules</div> <div class="metric-value">$($Report.TotalRules)</div> </div> <div class="metric"> <div class="metric-label">Compliant</div> <div class="metric-value compliant">$($Report.CompliantRules)</div> </div> <div class="metric"> <div class="metric-label">Non-Compliant</div> <div class="metric-value non-compliant">$($Report.NonCompliantRules)</div> </div> <div class="metric"> <div class="metric-label">Compliance Rate</div> <div class="metric-value">$($Report.CompliancePercentage)%</div> </div> </div> <h2>Compliance by Category</h2> <table> <thead> <tr> <th>Category</th> <th>Total</th> <th>Compliant</th> <th>Non-Compliant</th> <th>Compliance %</th> </tr> </thead> <tbody> "@ foreach ($category in $Report.CategoryBreakdown.Keys | Sort-Object) { $stats = $Report.CategoryBreakdown[$category] $html += @" <tr class="category-row"> <td>$category</td> <td>$($stats.Total)</td> <td class="compliant">$($stats.Compliant)</td> <td class="non-compliant">$($stats.NonCompliant)</td> <td>$($stats.Percentage)%</td> </tr> "@ } $html += @" </tbody> </table> "@ if ($IncludeRuleDetails) { $html += @" <h2>Rule Details</h2> <table> <thead> <tr> <th>Rule Name</th> <th>Category</th> <th>Severity</th> <th>Status</th> </tr> </thead> <tbody> "@ foreach ($rule in $Report.RuleResults) { $statusClass = if ($rule.Compliant) { 'compliant' } else { 'non-compliant' } $statusText = if ($rule.Compliant) { 'Compliant' } else { 'Non-Compliant' } $html += @" <tr> <td>$($rule.RuleName)</td> <td>$($rule.Category)</td> <td>$($rule.Severity)</td> <td class="$statusClass">$statusText</td> </tr> "@ } $html += @" </tbody> </table> "@ } if ($IncludeTrending -and $HistoricalReports) { $html += _GenerateTrendingSection -CurrentReport $Report -HistoricalReports $HistoricalReports } $html += @" <div class="footer"> <p>WinHarden Windows Hardening System</p> <p>Profile: $($Report.Profile) | Target: $($Report.TargetSystem)</p> </div> </div> </body> </html> "@ $html } function _GenerateTextReport { <# .SYNOPSIS Internal helper: Generates human-readable text compliance report for console output. #> param( [PSCustomObject]$Report, [bool]$IncludeRuleDetails, [bool]$IncludeTrending, [PSCustomObject[]]$HistoricalReports ) $text = @" ================================================================================ WinHarden HARDENING COMPLIANCE REPORT ================================================================================ Report Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') Profile: $($Report.Profile) Target System: $($Report.TargetSystem) Session ID: $($Report.SessionId) ================================================================================ COMPLIANCE SUMMARY ================================================================================ Status: $($Report.Status) Total Rules: $($Report.TotalRules) Compliant Rules: $($Report.CompliantRules) Non-Compliant Rules: $($Report.NonCompliantRules) Compliance Rate: $($Report.CompliancePercentage)% ================================================================================ CATEGORY BREAKDOWN ================================================================================ "@ foreach ($category in $Report.CategoryBreakdown.Keys | Sort-Object) { $stats = $Report.CategoryBreakdown[$category] $text += @" $category Total Rules: $($stats.Total) Compliant: $($stats.Compliant) Non-Compliant: $($stats.NonCompliant) Compliance Rate: $($stats.Percentage)% "@ } if ($IncludeRuleDetails) { $text += @" ================================================================================ RULE DETAILS ================================================================================ "@ foreach ($rule in $Report.RuleResults) { $status = if ($rule.Compliant) { 'COMPLIANT' } else { 'NON-COMPLIANT' } $text += @" Rule: $($rule.RuleName) Category: $($rule.Category) Severity: $($rule.Severity) Status: $status "@ } } if ($IncludeTrending -and $HistoricalReports) { $text += _GenerateTrendingTextSection -CurrentReport $Report -HistoricalReports $HistoricalReports } $text += @" ================================================================================ END OF REPORT ================================================================================ "@ $text } function _GenerateTrendingSection { <# .SYNOPSIS Internal helper: Generates trending data section with compliance velocity and trend direction for HTML reports. #> param( [PSCustomObject]$CurrentReport, [PSCustomObject[]]$HistoricalReports ) if ($null -eq $HistoricalReports -or $HistoricalReports.Count -eq 0) { return "" } $sorted = @($HistoricalReports | Sort-Object -Property VerificationTime) $previous = $sorted[-1] $percentDiff = $CurrentReport.CompliancePercentage - $previous.CompliancePercentage $trend = if ($percentDiff -gt 0) { "UP" } elseif ($percentDiff -lt 0) { "DOWN" } else { "STABLE" } $html = @" <h2>Compliance Trending</h2> <table> <thead> <tr> <th>Date</th> <th>Compliance %</th> <th>Compliant Rules</th> <th>Status</th> </tr> </thead> <tbody> "@ foreach ($report in $sorted + @($CurrentReport)) { $html += @" <tr> <td>$(($report.VerificationTime).ToString('yyyy-MM-dd HH:mm'))</td> <td>$($report.CompliancePercentage)%</td> <td>$($report.CompliantRules)/$($report.TotalRules)</td> <td>$($report.Status)</td> </tr> "@ } $html += @" </tbody> </table> <p>Trend: <strong>$trend</strong> | Change: $([Math]::Abs($percentDiff))%</p> "@ $html } function _GenerateTrendingTextSection { param( [PSCustomObject]$CurrentReport, [PSCustomObject[]]$HistoricalReports ) if ($null -eq $HistoricalReports -or $HistoricalReports.Count -eq 0) { return "" } $sorted = @($HistoricalReports | Sort-Object -Property VerificationTime) $previous = $sorted[-1] $percentDiff = $CurrentReport.CompliancePercentage - $previous.CompliancePercentage $trend = if ($percentDiff -gt 0) { "IMPROVING" } elseif ($percentDiff -lt 0) { "DECLINING" } else { "STABLE" } $text = @" ================================================================================ COMPLIANCE TRENDING ================================================================================ Trend: $trend Change: $([Math]::Abs($percentDiff))% Previous Score: $($previous.CompliancePercentage)% Current Score: $($CurrentReport.CompliancePercentage)% "@ $text } |