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 strictly read-only preflight. It checks the Windows platform, the selected
    checklist, administrator access, remote-session context, and whether Windows provides
    the commands needed by each checklist item. It does not evaluate policy settings and
    never changes the endpoint.
 
    EndpointForge calls its list of expected settings a baseline. In beginner-facing
    guidance, this command calls it a checklist: the list of Windows settings that the PC
    will be compared with. Selecting a checklist does not 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

    $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.')
    }

    $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. Checking only reads settings and does not change anything.' }
    }
    $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) setting check(s). A checklist is the list of expected Windows settings; selecting it does not 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 = '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 the list of expected Windows settings that the PC is compared with. Selecting or checking it does not apply changes.'
        ControlCount                  = $controls.Count
        AvailableControlCount         = $capabilities.Count - $unavailableControlCount
        UnavailableControlCount       = $unavailableControlCount
        PrivilegedCheckCount          = $privilegedCheckCount
        AutomaticFixCount             = $automaticFixCount
        AvailableAutomaticFixCount    = $availableAutomaticFixCount
        FixNowCount                   = $fixNowCount
        Summary                       = $summary
        NextStep                      = $nextStep
        Limitations                   = @($limitations)
        Checks                        = @($checks)
        ControlCapabilities           = @($capabilities)
    }
}