categories/Services.ps1

# Services category: curated, risk-rated optional-service catalog (research.md - 36 services
# cross-referenced against multiple independent public sources). Not already covered by
# telemetry/oem-suites - FR-037.

$script:OctaServicesCatalog = @(
    # Safe - target Disabled, no working-functionality loss if genuinely unused
    @{ Name = 'MapsBroker'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'WpcMonSvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'PhoneSvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'RetailDemo'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'wisvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'icssvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'XboxGipSvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'XblAuthManager'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'XblGameSave'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'XboxNetApiSvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'Fax'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'WalletService'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'TabletInputService'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'lfsvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'WerSvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'SCardSvr'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'ScDeviceEnum'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'SCPolicySvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'BTAGService'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'bthserv'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'SensorService'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'WbioSrvc'; Target = 'Disabled'; RiskLevel = 'Safe' }
    @{ Name = 'DevicePickerUserSvc'; Target = 'Disabled'; RiskLevel = 'Safe'; IsUserTemplate = $true }

    # Moderate - target Manual, real functionality trade-off if actually used
    @{ Name = 'Spooler'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'DPS'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'WdiServiceHost'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'WdiSystemHost'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'DiagsSvc'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'SessionEnv'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'TermService'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'UmRdpService'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'DoSvc'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'WorkFolders'; Target = 'Manual'; RiskLevel = 'Moderate' }
    @{ Name = 'Netlogon'; Target = 'Manual'; RiskLevel = 'Moderate' }

    # Risky - real, narrow chance of a serious problem
    @{ Name = 'BDESVC'; Target = 'Disabled'; RiskLevel = 'Risky' }
    @{ Name = 'AssignedAccessManagerSvc'; Target = 'Disabled'; RiskLevel = 'Risky' }
)

function Get-OctaServicesCategory {
    [CmdletBinding()]
    param()
    return [pscustomobject]@{
        Id                          = 'services'
        DisplayName                 = 'Services'
        Description                 = 'Curated, risk-rated optional Windows services'
        RequiresElevation           = $true
        ContainsIrreversibleActions = $false
        GetActionsFunction          = 'Get-OctaServicesActions'
        ApplyActionFunction         = 'Set-OctaServicesAction'
    }
}

function Get-OctaServicesActions {
    [CmdletBinding()]
    param()
    $actions = @()

    foreach ($entry in $script:OctaServicesCatalog) {
        if ($entry.IsUserTemplate) {
            $matches = @(Get-CimInstance -ClassName Win32_Service -ErrorAction SilentlyContinue |
                Where-Object { $_.Name -match "^$($entry.Name)_" })
        }
        else {
            $matches = @(Get-CimInstance -ClassName Win32_Service -Filter "Name='$($entry.Name)'" -ErrorAction SilentlyContinue)
        }

        foreach ($svc in $matches) {
            if ($svc.StartMode -eq $entry.Target) { continue }
            $actions += New-OctaAction -TargetType Service -TargetIdentifier $svc.Name `
                -CurrentValue $svc.StartMode -PlannedValue $entry.Target -Reversible $true -RiskLevel $entry.RiskLevel
        }
    }

    return $actions
}

function Set-OctaServicesAction {
    [CmdletBinding()]
    param([Parameter(Mandatory)]$Action)
    Set-Service -Name $Action.TargetIdentifier -StartupType $Action.PlannedValue
}