Public/Get-EFEndpointReadiness.ps1
|
function Get-EFEndpointReadiness { <# .SYNOPSIS Explains what EndpointForge can check and safely offer to fix in the current PowerShell window. .DESCRIPTION Performs a non-changing preflight. It checks the Windows platform, the selected checklist, administrator access, remote-session context, and whether Windows provides the features needed by each checklist item. It does not run the checklist. It identifies network-active items because running them later can contact a named server, name-resolution service, web address, configured Windows Update service, or identity provider while resolving a requested local-group account name. EndpointForge calls its list of expected settings a baseline. In user-friendly guidance, this command calls it a checklist: a list of things expected to be true, including settings, updates, storage, applications, jobs, files, certificates, events, account relationships, or network availability. Selecting a checklist does not run it or apply it. .PARAMETER Baseline The checklist to inspect. Supply the built-in name, a custom JSON path, or a validated checklist object. The default is the conservative EnterpriseRecommended checklist. .PARAMETER ControlId Limits readiness discovery to specific checklist item identifiers. .EXAMPLE Get-EFEndpointReadiness .EXAMPLE Get-EFEndpointReadiness -Baseline .\Contoso.Workstation.json .OUTPUTS EndpointForge.EndpointReadiness .LINK Get-EFEndpointSummary .LINK Get-EFRemediationPlan #> [CmdletBinding()] param( [Parameter(Position = 0)] [AllowNull()] [object]$Baseline = 'EnterpriseRecommended', [string[]]$ControlId ) $checkedAtUtc = [DateTime]::UtcNow $isWindowsPlatform = [bool](Test-EFWindows) $isAdministrator = $false if ($isWindowsPlatform) { $isAdministrator = [bool](Test-EFAdministrator) } $isRemoteSession = $false try { $senderInfo = Get-Variable -Name PSSenderInfo -ValueOnly -ErrorAction SilentlyContinue $isRemoteSession = $null -ne $senderInfo } catch { $isRemoteSession = $false } $resolvedBaseline = $null $baselineError = $null [object[]]$controls = @() try { $resolvedBaseline = Resolve-EFBaseline -Baseline $Baseline $controls = @($resolvedBaseline.Controls) if ($PSBoundParameters.ContainsKey('ControlId') -and $null -ne $ControlId -and $ControlId.Count -gt 0) { $missingIds = @($ControlId | Where-Object { $_ -notin $controls.Id }) if ($missingIds.Count -gt 0) { throw [System.ArgumentException]::new("The checklist does not contain item(s): $($missingIds -join ', ').") } $controls = @($controls | Where-Object { $_.Id -in @($ControlId) }) } } catch { $baselineError = $_.Exception.Message $resolvedBaseline = $null $controls = @() } $baselineIsValid = $null -ne $resolvedBaseline $checklistName = if ($baselineIsValid) { [string]$resolvedBaseline.Name } else { 'Unavailable' } $checklistVersion = if ($baselineIsValid) { [string]$resolvedBaseline.Version } else { $null } $capabilities = @( foreach ($control in $controls) { Get-EFControlCapability -Control $control -IsWindowsPlatform $isWindowsPlatform -IsAdministrator $isAdministrator } ) $unavailableControlCount = @($capabilities | Where-Object { -not $_.CanCheck }).Count $privilegedCheckCount = @($capabilities | Where-Object AdministratorRecommendedForCheck).Count $automaticFixCount = @($capabilities | Where-Object IsAutomaticFixDeclared).Count $availableAutomaticFixCount = @($capabilities | Where-Object AutomaticFixAvailable).Count $fixNowCount = @($capabilities | Where-Object CanFixNow).Count $networkCheckCount = @($controls | Where-Object { Test-EFControlUsesNetwork -Control $_ }).Count $assessmentReady = $isWindowsPlatform -and $baselineIsValid $completeCheckLikely = $assessmentReady -and $unavailableControlCount -eq 0 -and ($isAdministrator -or $privilegedCheckCount -eq 0) $fixReady = $assessmentReady -and $isAdministrator -and $availableAutomaticFixCount -gt 0 $limitations = [Collections.Generic.List[string]]::new() if (-not $isWindowsPlatform) { $limitations.Add('Endpoint checks require Windows 10, Windows 11, or a supported Windows Server release.') } if (-not $baselineIsValid) { $limitations.Add("The selected checklist could not be used: $baselineError") } if ($assessmentReady -and -not $isAdministrator -and $privilegedCheckCount -gt 0) { $limitations.Add("Windows may hide details for $privilegedCheckCount checklist item(s) until PowerShell is opened using Run as administrator.") } if ($assessmentReady -and -not $isAdministrator -and $automaticFixCount -gt 0) { $limitations.Add('This standard-user PowerShell window can check settings, but it cannot apply automatic fixes.') } if ($unavailableControlCount -gt 0) { $limitations.Add("Windows features needed by $unavailableControlCount checklist item(s) are unavailable in this session.") } if ($isRemoteSession) { $limitations.Add('This is a remote PowerShell session. Checks and approved fixes affect the remote PC, not the PC in front of you.') } if ($networkCheckCount -gt 0) { $limitations.Add( "$networkCheckCount checklist item(s) can contact an approved TCP destination, DNS service, web address, or configured update service when run. Those systems or network monitoring tools may record the activity." ) } $status = if (-not $assessmentReady) { 'Blocked' } elseif (-not $completeCheckLikely -or ($automaticFixCount -gt 0 -and -not $isAdministrator) -or $availableAutomaticFixCount -lt $automaticFixCount) { 'Limited' } else { 'Ready' } $summary = switch ($status) { 'Blocked' { 'EndpointForge cannot start this checklist until the blocked item is corrected.' } 'Limited' { 'EndpointForge can check this PC now, but some details or automatic fixes may be unavailable.' } default { 'EndpointForge is ready to check this PC. The check does not change Windows; review the Network activity line before allowing any network-active items.' } } $nextStep = if (-not $isWindowsPlatform) { 'Run EndpointForge on the Windows PC you want to check.' } elseif (-not $baselineIsValid) { 'Choose the built-in recommended checklist or correct the custom checklist file, then run readiness again.' } elseif (-not $isAdministrator -and ($privilegedCheckCount -gt 0 -or $automaticFixCount -gt 0)) { 'You can run a check now. For the most complete result, reopen PowerShell using Run as administrator and check again.' } elseif ($unavailableControlCount -gt 0) { 'Run the check and review unavailable items. They may not apply to this Windows edition or device type.' } else { 'Run Get-EFEndpointSummary, or choose Check this PC from the guided menu.' } $checks = [Collections.Generic.List[object]]::new() $checks.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.ReadinessCheck' Name = 'Windows PC' Status = if ($isWindowsPlatform) { 'Ready' } else { 'Blocked' } PlainLanguage = if ($isWindowsPlatform) { 'This PowerShell window is running on Windows.' } else { 'EndpointForge endpoint checks must run on Windows.' } NextStep = if ($isWindowsPlatform) { 'No action is needed.' } else { 'Open PowerShell on the Windows PC you want to check.' } }) $checks.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.ReadinessCheck' Name = 'Checklist' Status = if ($baselineIsValid) { 'Ready' } else { 'Blocked' } PlainLanguage = if ($baselineIsValid) { "'$checklistName' is valid and contains $($controls.Count) item(s). A checklist is a list of things expected to be true; selecting it does not run checks or apply changes." } else { "The selected checklist could not be read or validated: $baselineError" } NextStep = if ($baselineIsValid) { 'No action is needed.' } else { 'Choose a valid built-in checklist or custom JSON checklist file.' } }) $checks.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.ReadinessCheck' Name = 'PowerShell access' Status = if ($isAdministrator) { 'Ready' } elseif ($assessmentReady) { 'Warning' } else { 'Blocked' } PlainLanguage = if ($isAdministrator) { 'PowerShell is running as Administrator. Complete checks and guarded fixes can be offered when Windows supports them.' } else { 'PowerShell is running as a standard user. Normal checks work, but Windows may hide protected details and EndpointForge will not apply fixes.' } NextStep = if ($isAdministrator) { 'No action is needed.' } else { 'Check now, or use Run as administrator if you need complete protected details or later approve a fix.' } }) $checks.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.ReadinessCheck' Name = 'Windows features' Status = if (-not $assessmentReady) { 'Blocked' } elseif ($unavailableControlCount -gt 0) { 'Warning' } else { 'Ready' } PlainLanguage = if (-not $assessmentReady) { 'Windows feature discovery will begin after the platform and checklist are ready.' } elseif ($unavailableControlCount -gt 0) { "$unavailableControlCount checklist item(s) need a Windows feature that is unavailable in this session. EndpointForge will report those items instead of guessing." } else { 'The Windows commands needed by every selected checklist item are available.' } NextStep = if ($unavailableControlCount -gt 0) { 'Review ControlCapabilities for the exact unavailable feature.' } else { 'No action is needed.' } }) $checks.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.ReadinessCheck' Name = 'Network activity' Status = if ($networkCheckCount -gt 0) { 'Warning' } elseif ($assessmentReady) { 'Ready' } else { 'Blocked' } PlainLanguage = if ($networkCheckCount -gt 0) { "$networkCheckCount checklist item(s) can contact a named TCP destination, DNS service, web address, configured update service, or identity provider for the requested account when the checklist is run. The activity may be recorded." } elseif ($assessmentReady) { 'This checklist does not contain network-active checks.' } else { 'Network activity can be described after the checklist is ready.' } NextStep = if ($networkCheckCount -gt 0) { 'Review every network-active item and confirm that its destination, requested identity, and purpose are approved.' } else { 'No action is needed.' } }) $checks.Add([pscustomobject]@{ PSTypeName = 'EndpointForge.ReadinessCheck' Name = 'Target PC' Status = if ($isRemoteSession) { 'Warning' } else { 'Ready' } PlainLanguage = if ($isRemoteSession) { 'This PowerShell window is connected to another PC. Every check and approved fix affects that remote PC.' } else { 'This PowerShell window is checking the local PC.' } NextStep = if ($isRemoteSession) { 'Confirm the remote computer name before approving any future change.' } else { 'No action is needed.' } }) [pscustomobject]@{ PSTypeName = 'EndpointForge.EndpointReadiness' ComputerName = if ([string]::IsNullOrWhiteSpace($env:COMPUTERNAME)) { [Environment]::MachineName } else { $env:COMPUTERNAME } CheckedAtUtc = $checkedAtUtc Status = $status AssessmentReady = $assessmentReady CompleteCheckLikely = $completeCheckLikely FixReady = $fixReady IsWindows = $isWindowsPlatform IsAdministrator = $isAdministrator IsRemoteSession = $isRemoteSession ChecklistName = $checklistName ChecklistVersion = $checklistVersion ChecklistDefinition = 'A checklist (called a baseline in automation) is a list of things expected to be true, such as settings, files, recent events, and network availability. Selecting it does not run checks or apply changes.' ControlCount = $controls.Count AvailableControlCount = $capabilities.Count - $unavailableControlCount UnavailableControlCount = $unavailableControlCount PrivilegedCheckCount = $privilegedCheckCount AutomaticFixCount = $automaticFixCount AvailableAutomaticFixCount = $availableAutomaticFixCount FixNowCount = $fixNowCount NetworkCheckCount = $networkCheckCount Summary = $summary NextStep = $nextStep Limitations = @($limitations) Checks = @($checks) ControlCapabilities = @($capabilities) } } |