Public/Get-EFComplianceReport.ps1
|
function Get-EFComplianceReport { <# .SYNOPSIS Checks this computer against a Windows settings checklist. .DESCRIPTION Reads each selected Windows setting and compares it with the expected value in a checklist. It does not change Windows. In script output, a checklist is called a baseline and each checklist item is called a control. Those names are retained so existing PowerShell automation remains compatible. The report includes results, a score, guidance, and a predictable ExitCode for scripts: 0 when checked items match, 2 when an item does not match, or 3 when one or more items could not be checked. Use Test-EFEndpointCompliance when a simple Boolean answer is needed. .PARAMETER Baseline The checklist to use: a built-in name, a checklist JSON file, or an object returned by Get-EFBaseline. .PARAMETER ControlId One or more checklist item IDs to check. Every item is checked by default. .PARAMETER NoProgress Suppresses the progress display for non-interactive automation hosts. .EXAMPLE Get-EFComplianceReport .EXAMPLE $report = Get-EFComplianceReport -Baseline .\contoso-baseline.json $report.Results | Where-Object Status -ne 'Compliant' .OUTPUTS EndpointForge.ComplianceReport #> [CmdletBinding()] param( [Parameter(Position = 0)] [AllowNull()] [object]$Baseline = 'EnterpriseRecommended', [string[]]$ControlId, [switch]$NoProgress ) $null = Test-EFWindows -Throw $correlationId = [guid]::NewGuid().ToString() $resolvedBaseline = Resolve-EFBaseline -Baseline $Baseline $controls = @($resolvedBaseline.Controls) if ($PSBoundParameters.ContainsKey('ControlId') -and $null -ne $ControlId -and $ControlId.Length -gt 0) { $missingIds = @($ControlId | Where-Object { $_ -notin $controls.Id }) if ($missingIds.Count -gt 0) { throw [System.ArgumentException]::new("Unknown control Id(s): $($missingIds -join ', ')") } $controls = @($controls | Where-Object { $_.Id -in @($ControlId) }) } Write-EFLog -Message "Compliance evaluation started for baseline '$($resolvedBaseline.Name)'." ` -CorrelationId $correlationId -Data @{ controlCount = $controls.Count } $controlIndex = 0 $baselineCommandArgument = Get-EFBaselineCommandArgument -Baseline $resolvedBaseline $results = @( foreach ($control in $controls) { $controlIndex++ if (-not $NoProgress) { Write-Progress -Id 1102 -Activity 'EndpointForge Windows settings check' ` -Status "Checking: $($control.Title)" ` -PercentComplete ([math]::Round(($controlIndex / $controls.Count) * 100)) } $controlResult = Get-EFControlState -Control $control if ($controlResult.Status -eq 'NonCompliant' -and $controlResult.Remediable) { $controlResult.RecommendedAction = if ($null -ne $baselineCommandArgument) { "Preview this supported fix before approval. For scripts: Invoke-EFEndpointRemediation $baselineCommandArgument -ControlId '$($controlResult.ControlId)' -WhatIf" } else { 'Create a fix plan from this same in-memory checklist, then preview the supported change before approval.' } } $controlResult } ) if (-not $NoProgress) { Write-Progress -Id 1102 -Activity 'EndpointForge Windows settings check' -Completed } $compliantCount = @($results | Where-Object Status -eq 'Compliant').Count $nonCompliantCount = @($results | Where-Object Status -eq 'NonCompliant').Count $notApplicableCount = @($results | Where-Object Status -eq 'NotApplicable').Count $errorCount = @($results | Where-Object Status -eq 'Error').Count $knownCount = $compliantCount + $nonCompliantCount $attemptedCount = $knownCount + $errorCount $score = if ($knownCount -eq 0) { 100 } else { [math]::Round(($compliantCount / $knownCount) * 100, 1) } $coveragePercent = if ($attemptedCount -eq 0) { 100 } else { [math]::Round(($knownCount / $attemptedCount) * 100, 1) } $dataStatus = if ($errorCount -eq 0) { 'Complete' } elseif ($knownCount -eq 0) { 'Failed' } else { 'Partial' } $isCompliant = $nonCompliantCount -eq 0 -and $errorCount -eq 0 $exitCode = if ($errorCount -gt 0) { 3 } elseif ($nonCompliantCount -gt 0) { 2 } else { 0 } $status = if ($nonCompliantCount -gt 0) { 'NonCompliant' } elseif ($errorCount -gt 0) { 'Incomplete' } else { 'Compliant' } $summaryText = if ($isCompliant) { "$compliantCount applicable checklist item(s) match the expected settings." } elseif ($errorCount -gt 0) { "The check is incomplete: $nonCompliantCount item(s) do not match and $errorCount could not be checked." } else { "$nonCompliantCount checklist item(s) need attention. $score% of the settings that could be checked match." } $nextStep = if ($isCompliant) { 'No supported fix is needed.' } elseif ($errorCount -gt 0) { 'Review the items that could not be checked. Some protected information requires PowerShell to be opened with Run as Administrator (also called an elevated session).' } else { 'Create a fix plan to see what EndpointForge can safely preview and what needs a person to review.' } $report = [pscustomobject]@{ PSTypeName = 'EndpointForge.ComplianceReport' ComputerName = $env:COMPUTERNAME BaselineName = [string]$resolvedBaseline.Name BaselineVersion = [string]$resolvedBaseline.Version ChecklistName = [string]$resolvedBaseline.Name ChecklistVersion = [string]$resolvedBaseline.Version ChecklistItemCount = $controls.Count CorrelationId = $correlationId EvaluatedAtUtc = [DateTime]::UtcNow IsCompliant = $isCompliant Status = $status Score = $score DataStatus = $dataStatus CoveragePercent = $coveragePercent ExitCode = $exitCode Summary = $summaryText NextStep = $nextStep CompliantCount = $compliantCount NonCompliantCount = $nonCompliantCount NotApplicableCount = $notApplicableCount ErrorCount = $errorCount Results = $results } Write-EFLog -Message "Compliance evaluation completed for baseline '$($resolvedBaseline.Name)'." ` -Level $(if ($isCompliant) { 'Information' } else { 'Warning' }) -CorrelationId $correlationId ` -Data @{ score = $score; exitCode = $exitCode; nonCompliant = $nonCompliantCount; errors = $errorCount } return $report } |