Public/Get-EFEndpointSummary.ps1
|
function Get-EFEndpointSummary { <# .SYNOPSIS Gets one combined endpoint health and compliance summary. .DESCRIPTION Provides the recommended first-run experience for EndpointForge. It combines device identity, inventory, operational health, pending restart, compliance, data coverage, actionable findings, and stable exit codes in one pipeline-friendly object. .PARAMETER Baseline A built-in baseline name, JSON path, or validated baseline object. .PARAMETER ControlId Limits compliance evaluation to selected controls. .PARAMETER IncludeSoftware Includes installed software in the nested Inventory object. .PARAMETER MinimumFreeSpacePercent The system-drive free-space warning threshold. .PARAMETER MaximumUptimeDays The endpoint uptime warning threshold. .PARAMETER NoProgress Suppresses the progress display for non-interactive automation hosts. .EXAMPLE Get-EFEndpointSummary .EXAMPLE Get-EFEndpointSummary -NoProgress | Show-EFEndpointSummary -Detailed .OUTPUTS EndpointForge.EndpointSummary #> [CmdletBinding()] param( [Parameter(Position = 0)] [AllowNull()] [object]$Baseline = 'EnterpriseRecommended', [string[]]$ControlId, [switch]$IncludeSoftware, [ValidateRange(1, 99)] [int]$MinimumFreeSpacePercent = 15, [ValidateRange(1, 3650)] [int]$MaximumUptimeDays = 30, [switch]$NoProgress ) $null = Test-EFWindows -Throw $timer = [Diagnostics.Stopwatch]::StartNew() $correlationId = [guid]::NewGuid().ToString() try { if (-not $NoProgress) { Write-Progress -Id 1100 -Activity 'EndpointForge endpoint summary' -Status 'Collecting inventory and health' -PercentComplete 10 } $health = Get-EFEndpointHealth -MinimumFreeSpacePercent $MinimumFreeSpacePercent ` -MaximumUptimeDays $MaximumUptimeDays -IncludeSoftware:$IncludeSoftware -NoProgress if (-not $NoProgress) { Write-Progress -Id 1100 -Activity 'EndpointForge endpoint summary' -Status 'Evaluating compliance' -PercentComplete 60 } $complianceParameters = @{ Baseline = $Baseline NoProgress = $true } if ($PSBoundParameters.ContainsKey('ControlId')) { $complianceParameters.ControlId = $ControlId } $compliance = Get-EFComplianceReport @complianceParameters if (-not $NoProgress) { Write-Progress -Id 1100 -Activity 'EndpointForge endpoint summary' -Status 'Prioritizing findings' -PercentComplete 90 } $findings = [Collections.Generic.List[object]]::new() foreach ($check in @($health.Checks | Where-Object { $_.Status -ne 'Healthy' -and $_.Id -ne 'InventoryCollection' })) { $suggestedAction = if ($check.Status -eq 'Unknown') { 'Review collection details and rerun elevated if the capability requires administrator privileges.' } else { 'Review the health check details and address the reported threshold or protection state.' } $findingTitle = switch -Wildcard ([string]$check.Id) { 'DiskFreeSpace' { 'System drive free space' } 'Uptime' { 'Endpoint uptime' } 'PendingReboot' { 'Pending restart' } 'Firewall*' { ([string]$check.Id).Replace('Firewall', '') + ' firewall profile' } 'DefenderRealTimeProtection' { 'Microsoft Defender real-time protection' } 'BitLockerProtection' { 'BitLocker system-drive protection' } default { [string]$check.Id } } $findings.Add([pscustomobject]@{ Source = 'Health' Id = [string]$check.Id Title = $findingTitle Severity = [string]$check.Severity Status = [string]$check.Status Message = [string]$check.Message SuggestedAction = $suggestedAction RequiresElevation = $false }) } foreach ($controlResult in @($compliance.Results | Where-Object Status -in @('NonCompliant', 'Error'))) { $requiresElevation = $controlResult.Status -eq 'Error' -and $controlResult.Message -match 'elevat|access.+denied|administrator|privilege' $findings.Add([pscustomobject]@{ Source = 'Compliance' Id = [string]$controlResult.ControlId Title = [string]$controlResult.Title Severity = [string]$controlResult.Severity Status = [string]$controlResult.Status Message = [string]$controlResult.Message SuggestedAction = [string]$controlResult.RecommendedAction RequiresElevation = [bool]$requiresElevation }) } $criticalComplianceCount = @($compliance.Results | Where-Object { $_.Status -eq 'NonCompliant' -and $_.Severity -eq 'Critical' }).Count $actualIssueCount = @($findings | Where-Object Status -notin @('Unknown', 'Error')).Count $unknownCount = @($findings | Where-Object Status -in @('Unknown', 'Error')).Count $overallStatus = if ($health.Status -eq 'Critical' -or $criticalComplianceCount -gt 0) { 'Critical' } elseif ($actualIssueCount -gt 0 -or $health.Status -eq 'Warning') { 'Warning' } elseif ($unknownCount -gt 0) { 'Incomplete' } else { 'Healthy' } $complianceStatus = if ($compliance.NonCompliantCount -gt 0) { 'NonCompliant' } elseif ($compliance.ErrorCount -gt 0) { 'Incomplete' } else { 'Compliant' } $dataStatus = if ($health.DataStatus -eq 'Failed' -or $compliance.DataStatus -eq 'Failed') { 'Failed' } elseif ($health.DataStatus -eq 'Partial' -or $compliance.DataStatus -eq 'Partial') { 'Partial' } else { 'Complete' } $coveragePercent = [math]::Round(($health.CoveragePercent + $compliance.CoveragePercent) / 2, 1) $score = [math]::Round(($health.Score + $compliance.Score) / 2, 1) $exitCode = if ($dataStatus -eq 'Failed' -or $compliance.ErrorCount -gt 0) { 3 } elseif ($overallStatus -eq 'Critical' -or $compliance.NonCompliantCount -gt 0) { 2 } elseif ($overallStatus -eq 'Warning' -or $dataStatus -eq 'Partial') { 1 } else { 0 } $inventory = $health.Inventory $firewallProfiles = @($inventory.Security.Firewall) $enabledFirewallProfiles = @($firewallProfiles | Where-Object Enabled).Count $defender = $inventory.Security.Defender $bitLocker = $inventory.Security.BitLocker $security = [pscustomobject]@{ Firewall = if ($firewallProfiles.Count -eq 0) { 'Unknown' } else { "$enabledFirewallProfiles/$($firewallProfiles.Count) enabled" } Defender = if ($null -eq $defender) { 'Unknown' } elseif ($defender.RealTimeProtectionEnabled) { 'Enabled' } else { 'Disabled' } BitLocker = if ($null -eq $bitLocker) { 'Unknown' } else { [string]$bitLocker.ProtectionStatus } SecureBoot = if ($null -eq $inventory.Security.SecureBoot) { 'Unknown' } elseif ($inventory.Security.SecureBoot) { 'Enabled' } else { 'Disabled' } Tpm = if ($null -eq $inventory.Security.Tpm) { 'Unknown' } elseif ($inventory.Security.Tpm.Ready) { 'Ready' } else { 'NotReady' } } $sortedFindings = @($findings | Sort-Object ` @{ Expression = { switch ($_.Severity) { 'Critical' { 0 } 'High' { 1 } 'Warning' { 2 } 'Medium' { 3 } 'Low' { 4 } default { 5 } } } }, Source, Id) $nextStep = if ($compliance.ErrorCount -gt 0) { 'Resolve incomplete checks, often by running elevated, then run Get-EFEndpointSummary again.' } elseif ($compliance.NonCompliantCount -gt 0) { 'Run Get-EFRemediationPlan, then preview approved changes with Invoke-EFEndpointRemediation -WhatIf.' } elseif ($health.Status -ne 'Healthy') { 'Review Findings and address the health warnings before the next assessment.' } else { 'No action is required.' } $timer.Stop() [pscustomobject]@{ PSTypeName = 'EndpointForge.EndpointSummary' SchemaVersion = '1.0' ModuleVersion = [string](Get-Module EndpointForge).Version ComputerName = $inventory.ComputerName OverallStatus = $overallStatus HealthStatus = $health.Status ComplianceStatus = $complianceStatus DataStatus = $dataStatus Score = $score HealthScore = $health.Score ComplianceScore = $compliance.Score CoveragePercent = $coveragePercent IssueCount = $actualIssueCount UnknownCount = $unknownCount IsRebootPending = $health.PendingReboot.IsRebootPending ExitCode = $exitCode OperatingSystem = $inventory.OperatingSystemName OperatingSystemBuild = $inventory.OperatingSystemBuild Model = $inventory.DeviceModel UptimeDays = $inventory.UptimeDays DiskFreePercent = $inventory.SystemDriveFreePercent Security = $security NextStep = $nextStep CorrelationId = $correlationId StartedAtUtc = [DateTime]::UtcNow.Subtract($timer.Elapsed) CompletedAtUtc = [DateTime]::UtcNow DurationMilliseconds = $timer.ElapsedMilliseconds Findings = $sortedFindings Inventory = $inventory Health = $health Compliance = $compliance } } finally { if (-not $NoProgress) { Write-Progress -Id 1100 -Activity 'EndpointForge endpoint summary' -Completed } if ($timer.IsRunning) { $timer.Stop() } } } |