Skip to content

Optimizae MSFT Windows

● cpx October 2, 2026 6 min read awsome-windows
powershell
<#
.SYNOPSIS
    Disables Windows background services recommended by How-To Geek, with backup and restore.

.DESCRIPTION
    Core set (always applied):
        Windows Mobile Hotspot, Connected User Experiences and Telemetry,
        Windows Error Reporting, SysMain, Delivery Optimization (peer sharing).
    Optional sets are enabled with switches. Original startup types are saved to a
    JSON backup before any change, and -Restore puts everything back.

.PARAMETER IncludeSearch
    Also disable Windows Search (indexing). Start menu and Explorer search become slower.
.PARAMETER IncludeRemoteDesktop
    Also disable the three Remote Desktop services. Nobody can RDP into this PC afterwards.
.PARAMETER IncludeBiometrics
    Also disable Windows Biometric Service. Breaks Windows Hello face and fingerprint sign-in.
.PARAMETER IncludeExtras
    Also disable Bluetooth, Telephony, Fax, Camera Frame Server and Xbox services.
.PARAMETER All
    Apply every set above.
.PARAMETER Restore
    Restore all startup types recorded in the backup file.
.PARAMETER BackupPath
    Location of the JSON backup.

.EXAMPLE
    .\Disable-BackgroundServices.ps1 -WhatIf
.EXAMPLE
    .\Disable-BackgroundServices.ps1 -IncludeSearch -IncludeRemoteDesktop
.EXAMPLE
    .\Disable-BackgroundServices.ps1 -Restore
#>
[CmdletBinding(SupportsShouldProcess)]
param(
    [switch]$IncludeSearch,
    [switch]$IncludeRemoteDesktop,
    [switch]$IncludeBiometrics,
    [switch]$IncludeExtras,
    [switch]$All,
    [switch]$Restore,
    [string]$BackupPath = "$env:ProgramData\ServiceTweaks\services-backup.json"
)

$ErrorActionPreference = 'Stop'

# ---- Elevation check ---------------------------------------------------------
$principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
    throw 'Run this script from an elevated PowerShell session (Run as administrator).'
}

$svcRoot  = 'HKLM:\SYSTEM\CurrentControlSet\Services'
$doKey    = 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DeliveryOptimization'
$doValue  = 'DODownloadMode'   # 0 = HTTP only, no peer to peer sharing

# ---- Service catalogue -------------------------------------------------------
$groups = [ordered]@{
    Core = @(
        @{ Name = 'icssvc';    Label = 'Windows Mobile Hotspot Service' }
        @{ Name = 'DiagTrack'; Label = 'Connected User Experiences and Telemetry' }
        @{ Name = 'WerSvc';    Label = 'Windows Error Reporting Service' }
        @{ Name = 'SysMain';   Label = 'SysMain (Superfetch)' }
        @{ Name = 'DoSvc';     Label = 'Delivery Optimization' }
    )
    Search = @(
        @{ Name = 'WSearch';   Label = 'Windows Search' }
    )
    RemoteDesktop = @(
        @{ Name = 'TermService';  Label = 'Remote Desktop Services' }
        @{ Name = 'SessionEnv';   Label = 'Remote Desktop Configuration' }
        @{ Name = 'UmRdpService'; Label = 'Remote Desktop Services UserMode Port Redirector' }
    )
    Biometrics = @(
        @{ Name = 'WbioSrvc';  Label = 'Windows Biometric Service' }
    )
    Extras = @(
        @{ Name = 'bthserv';        Label = 'Bluetooth Support Service' }
        @{ Name = 'TapiSrv';        Label = 'Telephony' }
        @{ Name = 'Fax';            Label = 'Fax' }
        @{ Name = 'FrameServer';    Label = 'Windows Camera Frame Server' }
        @{ Name = 'XblAuthManager'; Label = 'Xbox Live Auth Manager' }
        @{ Name = 'XblGameSave';    Label = 'Xbox Live Game Save' }
        @{ Name = 'XboxNetApiSvc';  Label = 'Xbox Live Networking Service' }
        @{ Name = 'XboxGipSvc';     Label = 'Xbox Accessory Management Service' }
    )
}

# ---- Helpers -----------------------------------------------------------------
function Get-StartConfig([string]$Name) {
    $key = Join-Path $svcRoot $Name
    if (-not (Test-Path $key)) { return $null }
    $p = Get-ItemProperty -Path $key
    [pscustomobject]@{
        Start            = $p.Start
        DelayedAutostart = $p.DelayedAutostart
    }
}

function Format-StartType($Start, $Delayed) {
    switch ($Start) {
        0 { 'Boot' }
        1 { 'System' }
        2 { if ($Delayed -eq 1) { 'Automatic (Delayed)' } else { 'Automatic' } }
        3 { 'Manual' }
        4 { 'Disabled' }
        default { 'Unknown' }
    }
}

function Set-StartType([string]$Name, [int]$Start, $Delayed) {
    # Try the Service Control Manager first, fall back to the registry
    # (some services, such as DoSvc, refuse SCM changes on recent builds).
    $mode = switch ($Start) {
        2 { if ($Delayed -eq 1) { 'delayed-auto' } else { 'auto' } }
        3 { 'demand' }
        4 { 'disabled' }
    }
    $out = & sc.exe config $Name start= $mode 2>&1
    if ($LASTEXITCODE -eq 0) { return 'SCM' }

    $key = Join-Path $svcRoot $Name
    Set-ItemProperty -Path $key -Name Start -Value $Start -Type DWord
    if ($null -ne $Delayed) {
        Set-ItemProperty -Path $key -Name DelayedAutostart -Value ([int]$Delayed) -Type DWord
    }
    return 'Registry'
}

function Read-Backup {
    $data = @{}
    if (Test-Path $BackupPath) {
        (Get-Content $BackupPath -Raw | ConvertFrom-Json).PSObject.Properties |
            ForEach-Object { $data[$_.Name] = $_.Value }
    }
    return $data
}

function Save-Backup($Data) {
    $dir = Split-Path $BackupPath -Parent
    if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
    $Data | ConvertTo-Json -Depth 4 | Set-Content -Path $BackupPath -Encoding UTF8
}

# ---- Restore mode ------------------------------------------------------------
if ($Restore) {
    if (-not (Test-Path $BackupPath)) { throw "No backup found at $BackupPath" }
    $backup  = Read-Backup
    $results = foreach ($name in $backup.Keys) {
        $b = $backup[$name]

        if ($name -eq '__DOPolicy') {
            if ($PSCmdlet.ShouldProcess('Delivery Optimization policy', 'Restore')) {
                if ($b.Existed) {
                    Set-ItemProperty -Path $doKey -Name $doValue -Value ([int]$b.Value) -Type DWord
                } elseif (Test-Path $doKey) {
                    Remove-ItemProperty -Path $doKey -Name $doValue -ErrorAction SilentlyContinue
                }
            }
            [pscustomobject]@{ Service = 'DO policy'; Restored = 'Policy reverted'; Result = 'OK' }
            continue
        }

        $target = Format-StartType $b.Start $b.DelayedAutostart
        if (-not $PSCmdlet.ShouldProcess($name, "Restore startup type to $target")) { continue }
        try {
            $via = Set-StartType $name ([int]$b.Start) $b.DelayedAutostart
            if ($b.Start -eq 2) { Start-Service -Name $name -ErrorAction SilentlyContinue }
            [pscustomobject]@{ Service = $name; Restored = $target; Result = "OK ($via)" }
        } catch {
            [pscustomobject]@{ Service = $name; Restored = $target; Result = "FAILED: $($_.Exception.Message)" }
        }
    }
    $results | Format-Table -AutoSize
    if (-not $WhatIfPreference) {
        $stamp = Get-Date -Format 'yyyyMMdd-HHmmss'
        Rename-Item -Path $BackupPath -NewName "services-backup.restored-$stamp.json"
        Write-Host 'Restore complete. Reboot recommended.' -ForegroundColor Green
    }
    return
}

# ---- Build selection ---------------------------------------------------------
$selected = [System.Collections.Generic.List[hashtable]]::new()
$groups.Core | ForEach-Object { $selected.Add($_) }
if ($All -or $IncludeSearch)        { $groups.Search        | ForEach-Object { $selected.Add($_) } }
if ($All -or $IncludeRemoteDesktop) { $groups.RemoteDesktop | ForEach-Object { $selected.Add($_) } }
if ($All -or $IncludeBiometrics)    { $groups.Biometrics    | ForEach-Object { $selected.Add($_) } }
if ($All -or $IncludeExtras)        { $groups.Extras        | ForEach-Object { $selected.Add($_) } }

# ---- Disable -----------------------------------------------------------------
$backup  = Read-Backup   # merge, so a second run never overwrites the original values
$results = foreach ($svc in $selected) {
    $name = $svc.Name
    $cfg  = Get-StartConfig $name

    if ($null -eq $cfg) {
        [pscustomobject]@{ Service = $name; Description = $svc.Label; Before = '-'; Result = 'Not present' }
        continue
    }

    $before = Format-StartType $cfg.Start $cfg.DelayedAutostart
    if ($cfg.Start -eq 4) {
        [pscustomobject]@{ Service = $name; Description = $svc.Label; Before = $before; Result = 'Already disabled' }
        continue
    }

    if (-not $PSCmdlet.ShouldProcess("$($svc.Label) ($name)", 'Stop and disable')) { continue }

    if (-not $backup.ContainsKey($name)) { $backup[$name] = $cfg }

    try {
        Stop-Service -Name $name -Force -ErrorAction SilentlyContinue
        $via = Set-StartType $name 4 $null
        [pscustomobject]@{ Service = $name; Description = $svc.Label; Before = $before; Result = "Disabled ($via)" }
    } catch {
        [pscustomobject]@{ Service = $name; Description = $svc.Label; Before = $before; Result = "FAILED: $($_.Exception.Message)" }
    }
}

# Delivery Optimization: also set policy to HTTP only, so peer sharing stays off
# even if Windows re-enables the service during a feature update.
if ($PSCmdlet.ShouldProcess('Delivery Optimization policy', "Set $doValue = 0 (no peer sharing)")) {
    if (-not $backup.ContainsKey('__DOPolicy')) {
        $existing = $null
        if (Test-Path $doKey) { $existing = (Get-ItemProperty -Path $doKey -ErrorAction SilentlyContinue).$doValue }
        $backup['__DOPolicy'] = @{ Existed = ($null -ne $existing); Value = $existing }
    }
    if (-not (Test-Path $doKey)) { New-Item -Path $doKey -Force | Out-Null }
    Set-ItemProperty -Path $doKey -Name $doValue -Value 0 -Type DWord
}

if (-not $WhatIfPreference) { Save-Backup $backup }

$results | Format-Table -AutoSize
if (-not $WhatIfPreference) {
    Write-Host "Backup saved to $BackupPath" -ForegroundColor Cyan
    Write-Host 'Done. Reboot recommended. Undo with: .\Disable-BackgroundServices.ps1 -Restore' -ForegroundColor Green
}

Be the first to comment

protected · no tracking

0 comments