Private/Get-EFControlState.ps1

function Get-EFControlState {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [object]$Control
    )

    $type = [string](Get-EFPropertyValue -InputObject $Control -Name 'Type')
    $desiredValue = Get-EFPropertyValue -InputObject $Control -Name 'DesiredValue'

    try {
        switch ($type) {
            'Registry' {
                $path = [string](Get-EFPropertyValue -InputObject $Control -Name 'Path')
                $valueName = [string](Get-EFPropertyValue -InputObject $Control -Name 'ValueName')

                if (-not (Test-Path -LiteralPath $path)) {
                    return New-EFControlResult -Control $Control -Status NonCompliant -ActualValue $null `
                        -DesiredValue $desiredValue -Message "Registry path '$path' does not exist."
                }

                $item = Get-ItemProperty -LiteralPath $path -ErrorAction Stop
                $property = $item.PSObject.Properties[$valueName]
                if ($null -eq $property) {
                    return New-EFControlResult -Control $Control -Status NonCompliant -ActualValue $null `
                        -DesiredValue $desiredValue -Message "Registry value '$valueName' does not exist."
                }

                $actual = $property.Value
                $isMatch = Test-EFValueEqual -Actual $actual -Desired $desiredValue
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message "Registry value '$path\$valueName' evaluated."
            }

            'Service' {
                $name = [string](Get-EFPropertyValue -InputObject $Control -Name 'Name')
                $service = Get-Service -Name $name -ErrorAction SilentlyContinue
                if ($null -eq $service) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message "Service '$name' is not installed."
                }

                $startupType = Get-EFPropertyValue -InputObject $service -Name 'StartType'
                if ($null -eq $startupType) {
                    $escapedName = $name.Replace("'", "''")
                    $cimService = Get-CimInstance -ClassName Win32_Service -Filter "Name='$escapedName'" -ErrorAction Stop
                    $startupType = switch ([string]$cimService.StartMode) {
                        'Auto' { 'Automatic' }
                        default { [string]$cimService.StartMode }
                    }
                }

                $desiredStartupType = [string](Get-EFPropertyValue -InputObject $Control -Name 'StartupType')
                $desiredStatus = [string](Get-EFPropertyValue -InputObject $Control -Name 'Status')
                $startupMatches = [string]::IsNullOrWhiteSpace($desiredStartupType) -or
                    (Test-EFValueEqual -Actual ([string]$startupType) -Desired $desiredStartupType)
                $statusMatches = [string]::IsNullOrWhiteSpace($desiredStatus) -or
                    (Test-EFValueEqual -Actual ([string]$service.Status) -Desired $desiredStatus)

                $actual = [ordered]@{
                    StartupType = [string]$startupType
                    Status      = [string]$service.Status
                }
                $desired = [ordered]@{
                    StartupType = $desiredStartupType
                    Status      = $desiredStatus
                }
                $resultStatus = if ($startupMatches -and $statusMatches) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $resultStatus -ActualValue $actual `
                    -DesiredValue $desired -Message "Service '$name' evaluated."
            }

            'FirewallProfile' {
                if ($null -eq (Get-Command -Name Get-NetFirewallProfile -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'Windows Firewall cmdlets are unavailable.'
                }

                $name = [string](Get-EFPropertyValue -InputObject $Control -Name 'Name')
                $firewallProfile = Get-NetFirewallProfile -Name $name -ErrorAction Stop
                $actual = [bool]$firewallProfile.Enabled
                $isMatch = Test-EFValueEqual -Actual $actual -Desired $desiredValue
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message "Firewall profile '$name' evaluated."
            }

            'Defender' {
                if ($null -eq (Get-Command -Name Get-MpComputerStatus -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'Microsoft Defender cmdlets are unavailable.'
                }

                $propertyName = [string](Get-EFPropertyValue -InputObject $Control -Name 'Property')
                $defender = Get-MpComputerStatus -ErrorAction Stop
                $property = $defender.PSObject.Properties[$propertyName]
                if ($null -eq $property) {
                    throw "Microsoft Defender does not expose property '$propertyName'."
                }
                $actual = $property.Value
                $isMatch = Test-EFValueEqual -Actual $actual -Desired $desiredValue
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message "Microsoft Defender property '$propertyName' evaluated."
            }

            'WindowsOptionalFeature' {
                if ($null -eq (Get-Command -Name Get-WindowsOptionalFeature -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'Windows optional feature cmdlets are unavailable.'
                }

                $name = [string](Get-EFPropertyValue -InputObject $Control -Name 'Name')
                $feature = Get-WindowsOptionalFeature -Online -FeatureName $name -ErrorAction SilentlyContinue
                if ($null -eq $feature) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message "Windows optional feature '$name' is unavailable."
                }
                $actual = [string]$feature.State
                $isMatch = Test-EFValueEqual -Actual $actual -Desired $desiredValue
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message "Windows optional feature '$name' evaluated."
            }

            'FileExists' {
                $path = Resolve-EFLocalFilePath -Path ([string](Get-EFPropertyValue -InputObject $Control -Name 'Path')) `
                    -CheckExistingAncestors
                try {
                    $item = Get-Item -LiteralPath $path -Force -ErrorAction Stop
                    $actual = -not [bool]$item.PSIsContainer
                    $message = if ($actual) {
                        "The requested file exists at '$path'."
                    }
                    else {
                        "A folder exists at '$path', but the checklist asks for a file."
                    }
                }
                catch [System.Management.Automation.ItemNotFoundException] {
                    $actual = $false
                    $message = "No file exists at '$path'."
                }

                $status = if (Test-EFValueEqual -Actual $actual -Desired $desiredValue) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message $message
            }

            'FileContainsText' {
                $path = Resolve-EFLocalFilePath -Path ([string](Get-EFPropertyValue -InputObject $Control -Name 'Path')) `
                    -CheckExistingAncestors
                $text = [string](Get-EFPropertyValue -InputObject $Control -Name 'Text')
                $tailLines = [int](Get-EFPropertyValue -InputObject $Control -Name 'TailLines' -Default 2000)
                $caseSensitive = [bool](Get-EFPropertyValue -InputObject $Control -Name 'CaseSensitive' -Default $false)
                $encoding = [string](Get-EFPropertyValue -InputObject $Control -Name 'Encoding' -Default 'Utf8')
                $item = Get-Item -LiteralPath $path -Force -ErrorAction Stop
                if ([bool]$item.PSIsContainer) {
                    throw "The path '$path' is a folder. This checklist item requires a text file."
                }

                $beforeLength = [int64]$item.Length
                $beforeWriteTime = $item.LastWriteTimeUtc
                $tail = Read-EFBoundedTextTail -Path $path -TailLines $tailLines -Encoding $encoding
                $lines = @($tail.Lines)
                $afterItem = Get-Item -LiteralPath $path -Force -ErrorAction Stop
                if ([int64]$afterItem.Length -ne $beforeLength -or $afterItem.LastWriteTimeUtc -ne $beforeWriteTime) {
                    throw "The text file '$path' changed while EndpointForge was reading it. Run the check again for a trustworthy answer."
                }

                $comparison = if ($caseSensitive) { [StringComparison]::Ordinal } else { [StringComparison]::OrdinalIgnoreCase }
                $found = $false
                foreach ($line in $lines) {
                    if (([string]$line).IndexOf($text, $comparison) -ge 0) {
                        $found = $true
                        break
                    }
                }

                $status = if (Test-EFValueEqual -Actual $found -Desired $desiredValue) { 'Compliant' } else { 'NonCompliant' }
                $finding = if ($found) { 'was found' } else { 'was not found' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $found `
                    -DesiredValue $desiredValue -Message (
                        "Checked up to the most recent $tailLines lines in '$path'; the requested text $finding. File contents are not included in the result."
                    )
            }

            'WindowsEvent' {
                if ($null -eq (Get-Command -Name Get-WinEvent -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status Error -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'Windows event log commands are unavailable.'
                }

                $logName = [string](Get-EFPropertyValue -InputObject $Control -Name 'LogName')
                $eventIds = @((Get-EFPropertyValue -InputObject $Control -Name 'EventIds') | ForEach-Object { [int]$_ })
                $providerName = [string](Get-EFPropertyValue -InputObject $Control -Name 'ProviderName')
                $lookbackMinutes = [int](Get-EFPropertyValue -InputObject $Control -Name 'LookbackMinutes' -Default 60)
                $minimumCount = [int](Get-EFPropertyValue -InputObject $Control -Name 'MinimumCount' -Default 1)

                $eventLog = Get-WinEvent -ListLog $logName -ErrorAction Stop
                if ($null -eq $eventLog) {
                    throw "Windows did not return the event log '$logName'. Check the exact LogName."
                }
                if ((Test-EFPropertyPresent -InputObject $eventLog -Name 'IsEnabled') -and
                    -not [bool](Get-EFPropertyValue -InputObject $eventLog -Name 'IsEnabled')) {
                    throw "The Windows event log '$logName' is disabled, so it cannot provide trustworthy recent-event evidence."
                }
                $filter = @{
                    LogName  = $logName
                    Id       = [int[]]$eventIds
                    StartTime = [DateTime]::Now.AddMinutes(-$lookbackMinutes)
                }
                if (-not [string]::IsNullOrWhiteSpace($providerName)) {
                    $filter.ProviderName = $providerName
                }

                try {
                    $events = @(Get-WinEvent -FilterHashtable $filter -MaxEvents $minimumCount -ErrorAction Stop)
                }
                catch {
                    if ($_.FullyQualifiedErrorId -like 'NoMatchingEventsFound*' -or
                        $_.Exception.Message -match '^No events were found') {
                        $events = @()
                    }
                    else {
                        throw
                    }
                }

                $actual = $events.Count -ge $minimumCount
                $status = if (Test-EFValueEqual -Actual $actual -Desired $desiredValue) { 'Compliant' } else { 'NonCompliant' }
                $providerDescription = if ([string]::IsNullOrWhiteSpace($providerName)) { '' } else { " from '$providerName'" }
                $countDescription = if ($events.Count -ge $minimumCount) {
                    "Found at least $($events.Count) matching event(s)"
                }
                else {
                    "Found $($events.Count) matching event(s)"
                }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message (
                        "$countDescription$providerDescription in '$logName' during the last $lookbackMinutes minute(s); at least $minimumCount were requested. Event messages are not included in the result."
                    )
            }

            'TcpPort' {
                $hostName = [string](Get-EFPropertyValue -InputObject $Control -Name 'HostName')
                $port = [int](Get-EFPropertyValue -InputObject $Control -Name 'Port')
                $timeoutMilliseconds = [int](Get-EFPropertyValue -InputObject $Control -Name 'TimeoutMilliseconds' -Default 3000)
                $probe = Test-EFTcpPort -HostName $hostName -Port $port -TimeoutMilliseconds $timeoutMilliseconds
                if ($probe.FailureReason -eq 'NameResolutionFailed') {
                    throw "The host name '$hostName' could not be resolved. Check the spelling and DNS configuration."
                }
                if ([bool]$probe.IsEvaluationError) {
                    throw "Windows could not complete the TCP port check for '$hostName' on port $port ($($probe.FailureReason))."
                }

                $actual = [bool]$probe.Connected
                $status = if (Test-EFValueEqual -Actual $actual -Desired $desiredValue) { 'Compliant' } else { 'NonCompliant' }
                $outcome = if ($actual) { 'accepted a TCP connection' } else { "did not accept a TCP connection ($($probe.FailureReason))" }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message (
                        "'$hostName' port $port $outcome within $timeoutMilliseconds millisecond(s). EndpointForge opened and closed one TCP connection without sending application data."
                    )
            }

            'BitLocker' {
                if ($null -eq (Get-Command -Name Get-BitLockerVolume -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'BitLocker cmdlets are unavailable.'
                }

                $mountPoint = [Environment]::ExpandEnvironmentVariables(
                    [string](Get-EFPropertyValue -InputObject $Control -Name 'MountPoint' -Default '%SystemDrive%')
                )
                $volume = Get-BitLockerVolume -MountPoint $mountPoint -ErrorAction Stop
                $actual = [string]$volume.ProtectionStatus
                $isMatch = Test-EFValueEqual -Actual $actual -Desired $desiredValue
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message "BitLocker volume '$mountPoint' evaluated."
            }

            'SecureBoot' {
                if ($null -eq (Get-Command -Name Confirm-SecureBootUEFI -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'Secure Boot cmdlets are unavailable.'
                }

                try {
                    $actual = [bool](Confirm-SecureBootUEFI -ErrorAction Stop)
                }
                catch {
                    $secureBootMessage = $_.Exception.Message
                    $secureBootStatus = if ($secureBootMessage -match 'not supported|not available|not implemented') { 'NotApplicable' } else { 'Error' }
                    return New-EFControlResult -Control $Control -Status $secureBootStatus -ActualValue $null `
                        -DesiredValue $desiredValue -Message "Secure Boot cannot be queried on this device: $secureBootMessage"
                }
                $isMatch = Test-EFValueEqual -Actual $actual -Desired $desiredValue
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message 'Secure Boot evaluated.'
            }

            'Tpm' {
                if ($null -eq (Get-Command -Name Get-Tpm -ErrorAction SilentlyContinue)) {
                    return New-EFControlResult -Control $Control -Status NotApplicable -ActualValue $null `
                        -DesiredValue $desiredValue -Message 'TPM cmdlets are unavailable.'
                }

                $tpm = Get-Tpm -ErrorAction Stop
                if (-not (Test-EFPropertyPresent -InputObject $tpm -Name 'TpmPresent') -or
                    -not (Test-EFPropertyPresent -InputObject $tpm -Name 'TpmReady')) {
                    throw 'TPM status was not returned. Run PowerShell as Administrator to evaluate this control.'
                }
                $actual = [ordered]@{
                    TpmPresent = [bool](Get-EFPropertyValue -InputObject $tpm -Name 'TpmPresent')
                    TpmReady   = [bool](Get-EFPropertyValue -InputObject $tpm -Name 'TpmReady')
                }
                $expectedPresent = [bool](Get-EFPropertyValue -InputObject $desiredValue -Name 'TpmPresent' -Default $true)
                $expectedReady = [bool](Get-EFPropertyValue -InputObject $desiredValue -Name 'TpmReady' -Default $true)
                $isMatch = $actual.TpmPresent -eq $expectedPresent -and $actual.TpmReady -eq $expectedReady
                $status = if ($isMatch) { 'Compliant' } else { 'NonCompliant' }
                return New-EFControlResult -Control $Control -Status $status -ActualValue $actual `
                    -DesiredValue $desiredValue -Message 'Trusted Platform Module state evaluated.'
            }

            default {
                throw "Unsupported baseline control type '$type'."
            }
        }
    }
    catch {
        return New-EFControlResult -Control $Control -Status Error -ActualValue $null `
            -DesiredValue $desiredValue -Message $_.Exception.Message
    }
}