Skip to content

Instantly share code, notes, and snippets.

@ph20
Created April 8, 2026 19:27
Show Gist options
  • Select an option

  • Save ph20/c835d191b1c20944f1d11b6891850473 to your computer and use it in GitHub Desktop.

Select an option

Save ph20/c835d191b1c20944f1d11b6891850473 to your computer and use it in GitHub Desktop.
Minimizes Windows telemetry on Windows 10 Enterprise LTSC / LTSC N.
#requires -Version 5.1
#requires -RunAsAdministrator
<#
.SYNOPSIS
Minimizes Windows telemetry on Windows 10 Enterprise LTSC / LTSC N.
.DESCRIPTION
Applies supported policy-based settings first, disables common telemetry-related
scheduled tasks, and optionally disables telemetry services in aggressive mode.
Safe defaults:
- Uses policy keys where possible
- Disables common CEIP / telemetry tasks
- Leaves DiagTrack and dmwappushservice unchanged unless explicitly requested
Aggressive mode:
- Disables DiagTrack
- Disables dmwappushservice
.NOTES
Run this script from 64-bit Windows PowerShell as Administrator.
A reboot is recommended after the script finishes.
#>
[CmdletBinding()]
param(
[switch]$Aggressive,
[switch]$DisableDiagTrackService,
[switch]$DisableDmwAppPushService,
[string]$LogPath = "$env:ProgramData\Disable-WindowsTelemetry-LTSC.log"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
if ([Environment]::Is64BitOperatingSystem -and -not [Environment]::Is64BitProcess) {
throw 'Run this script in 64-bit Windows PowerShell.'
}
$DisableDiagTrackService = $DisableDiagTrackService -or $Aggressive
$DisableDmwAppPushService = $DisableDmwAppPushService -or $Aggressive
$script:LogPath = [System.IO.Path]::GetFullPath($LogPath)
$logDirectory = Split-Path -Path $script:LogPath -Parent
if ($logDirectory -and -not (Test-Path -LiteralPath $logDirectory)) {
New-Item -Path $logDirectory -ItemType Directory -Force | Out-Null
}
function Write-Log {
param(
[Parameter(Mandatory = $true)]
[string]$Message,
[ValidateSet('INFO', 'WARN', 'ERROR')]
[string]$Level = 'INFO'
)
$timestamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
$line = '[{0}] [{1}] {2}' -f $timestamp, $Level, $Message
Write-Host $line
Add-Content -LiteralPath $script:LogPath -Value $line -Encoding UTF8
}
function Ensure-RegistryKey {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
if (-not (Test-Path -LiteralPath $Path)) {
New-Item -Path $Path -Force | Out-Null
}
}
function Set-DwordValue {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[string]$Name,
[Parameter(Mandatory = $true)]
[int]$Value
)
Ensure-RegistryKey -Path $Path
$currentValue = $null
try {
$currentValue = (Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop).$Name
} catch {
$currentValue = $null
}
if ($currentValue -ne $Value) {
New-ItemProperty -LiteralPath $Path -Name $Name -PropertyType DWord -Value $Value -Force | Out-Null
Write-Log "Set $Path\$Name = $Value"
} else {
Write-Log "Already set: $Path\$Name = $Value"
}
}
function Get-DwordValue {
param(
[Parameter(Mandatory = $true)]
[string]$Path,
[Parameter(Mandatory = $true)]
[string]$Name
)
try {
return (Get-ItemProperty -LiteralPath $Path -Name $Name -ErrorAction Stop).$Name
} catch {
return $null
}
}
function Get-ServiceStartMode {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
try {
return (Get-CimInstance -ClassName Win32_Service -Filter "Name='$Name'" -ErrorAction Stop).StartMode
} catch {
return $null
}
}
function Disable-ServiceSafe {
param(
[Parameter(Mandatory = $true)]
[string]$Name
)
try {
$service = Get-Service -Name $Name -ErrorAction Stop
} catch {
Write-Log "Service not found: $Name"
return
}
try {
if ($service.Status -ne 'Stopped') {
Stop-Service -Name $Name -Force -ErrorAction Stop
Write-Log "Stopped service: $Name"
}
} catch {
Write-Log ("Could not stop service {0}: {1}" -f $Name, $_.Exception.Message) 'WARN'
}
try {
Set-Service -Name $Name -StartupType Disabled -ErrorAction Stop
Write-Log "Disabled service: $Name"
} catch {
try {
& sc.exe config $Name start= disabled | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Log "Disabled service with sc.exe: $Name"
} else {
Write-Log "sc.exe could not disable service $Name" 'WARN'
}
} catch {
Write-Log ("Could not disable service {0}: {1}" -f $Name, $_.Exception.Message) 'WARN'
}
}
}
function Disable-TaskSafe {
param(
[Parameter(Mandatory = $true)]
[string]$TaskName
)
try {
& schtasks.exe /Query /TN $TaskName | Out-Null
if ($LASTEXITCODE -ne 0) {
Write-Log "Task not found: $TaskName"
return
}
} catch {
Write-Log "Task not found: $TaskName"
return
}
try {
& schtasks.exe /Change /TN $TaskName /Disable | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Log "Disabled task: $TaskName"
} else {
Write-Log "Could not disable task: $TaskName" 'WARN'
}
} catch {
Write-Log ("Could not disable task {0}: {1}" -f $TaskName, $_.Exception.Message) 'WARN'
}
}
function Set-DwordValueForLoadedUsersAndDefaultProfile {
param(
[Parameter(Mandatory = $true)]
[string]$RelativePath,
[Parameter(Mandatory = $true)]
[string]$Name,
[Parameter(Mandatory = $true)]
[int]$Value
)
$userSidPattern = '^S-1-5-21-\d+-\d+-\d+-\d+$'
$loadedUserSids = Get-ChildItem -Path Registry::HKEY_USERS -ErrorAction SilentlyContinue |
Where-Object { $_.PSChildName -match $userSidPattern } |
Select-Object -ExpandProperty PSChildName
foreach ($sid in $loadedUserSids) {
$path = "Registry::HKEY_USERS\$sid\$RelativePath"
Set-DwordValue -Path $path -Name $Name -Value $Value
}
$defaultHiveFile = Join-Path $env:SystemDrive 'Users\Default\NTUSER.DAT'
$defaultMountName = 'HKU\DefaultTelemetryProfile'
$defaultMountPath = 'Registry::HKEY_USERS\DefaultTelemetryProfile'
if (Test-Path -LiteralPath $defaultHiveFile) {
$mountedHere = $false
try {
if (-not (Test-Path -LiteralPath $defaultMountPath)) {
& reg.exe load $defaultMountName $defaultHiveFile | Out-Null
if ($LASTEXITCODE -eq 0) {
$mountedHere = $true
Write-Log 'Loaded default user hive.'
} else {
Write-Log 'Could not load default user hive.' 'WARN'
}
}
if (Test-Path -LiteralPath $defaultMountPath) {
$defaultPath = "$defaultMountPath\$RelativePath"
Set-DwordValue -Path $defaultPath -Name $Name -Value $Value
}
} finally {
if ($mountedHere) {
& reg.exe unload $defaultMountName | Out-Null
if ($LASTEXITCODE -eq 0) {
Write-Log 'Unloaded default user hive.'
} else {
Write-Log 'Could not unload default user hive cleanly.' 'WARN'
}
}
}
} else {
Write-Log 'Default user hive not found.' 'WARN'
}
}
try {
Write-Log 'Starting telemetry hardening script.'
$currentVersion = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$productName = $currentVersion.ProductName
$editionId = $currentVersion.EditionID
$releaseId = $null
if ($currentVersion.PSObject.Properties.Name -contains 'DisplayVersion') {
$releaseId = $currentVersion.DisplayVersion
} elseif ($currentVersion.PSObject.Properties.Name -contains 'ReleaseId') {
$releaseId = $currentVersion.ReleaseId
}
Write-Log "Detected OS: $productName | EditionID: $editionId | Release: $releaseId"
if ($editionId -notmatch 'Enterprise|Education|Server') {
Write-Log 'AllowTelemetry=0 is not fully supported on this edition. Enterprise, Education, or Server is recommended.' 'WARN'
}
Write-Log 'Applying machine-wide policy settings.'
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'AllowTelemetry' -Value 0
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'DoNotShowFeedbackNotifications' -Value 1
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableWindowsConsumerFeatures' -Value 1
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\SQMClient\Windows' -Name 'CEIPEnable' -Value 0
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\AdvertisingInfo' -Name 'DisabledByGroupPolicy' -Value 1
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\InputPersonalization' -Name 'AllowInputPersonalization' -Value 0
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE' -Name 'DisablePrivacyExperience' -Value 1
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' -Name 'EnableActivityFeed' -Value 0
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' -Name 'PublishUserActivities' -Value 0
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' -Name 'UploadUserActivities' -Value 0
Set-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy' -Name 'LetAppsRunInBackground' -Value 2
Write-Log 'Applying user-scoped settings to loaded profiles and the default profile.'
Set-DwordValueForLoadedUsersAndDefaultProfile -RelativePath 'SOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableTailoredExperiencesWithDiagnosticData' -Value 1
Set-DwordValueForLoadedUsersAndDefaultProfile -RelativePath 'SOFTWARE\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy' -Name 'HasAccepted' -Value 0
Write-Log 'Disabling scheduled tasks related to telemetry and CEIP where present.'
$tasks = @(
'\Microsoft\Windows\Application Experience\Microsoft Compatibility Appraiser',
'\Microsoft\Windows\Application Experience\ProgramDataUpdater',
'\Microsoft\Windows\Application Experience\StartupAppTask',
'\Microsoft\Windows\Autochk\Proxy',
'\Microsoft\Windows\Customer Experience Improvement Program\Consolidator',
'\Microsoft\Windows\Customer Experience Improvement Program\KernelCeipTask',
'\Microsoft\Windows\Customer Experience Improvement Program\UsbCeip',
'\Microsoft\Windows\DiskDiagnostic\Microsoft-Windows-DiskDiagnosticDataCollector',
'\Microsoft\Windows\Feedback\Siuf\DmClient',
'\Microsoft\Windows\Feedback\Siuf\DmClientOnScenarioDownload'
)
foreach ($task in $tasks) {
Disable-TaskSafe -TaskName $task
}
if ($DisableDiagTrackService) {
Write-Log 'Disabling DiagTrack service.'
Disable-ServiceSafe -Name 'DiagTrack'
} else {
Write-Log 'DiagTrack was left unchanged. Use -DisableDiagTrackService or -Aggressive to disable it.'
}
if ($DisableDmwAppPushService) {
Write-Log 'Disabling dmwappushservice.'
Disable-ServiceSafe -Name 'dmwappushservice'
} else {
Write-Log 'dmwappushservice was left unchanged. Use -DisableDmwAppPushService or -Aggressive to disable it.'
}
Write-Log 'Verification summary:'
$summary = [ordered]@{
ProductName = $productName
EditionID = $editionId
Release = $releaseId
AllowTelemetry = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'AllowTelemetry'
DoNotShowFeedbackNotifications = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection' -Name 'DoNotShowFeedbackNotifications'
DisableWindowsConsumerFeatures = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableWindowsConsumerFeatures'
CEIPEnable = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\SQMClient\Windows' -Name 'CEIPEnable'
DisabledByGroupPolicy_AdvertisingId = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\AdvertisingInfo' -Name 'DisabledByGroupPolicy'
AllowInputPersonalization = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\InputPersonalization' -Name 'AllowInputPersonalization'
DisablePrivacyExperience = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\OOBE' -Name 'DisablePrivacyExperience'
EnableActivityFeed = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' -Name 'EnableActivityFeed'
PublishUserActivities = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' -Name 'PublishUserActivities'
UploadUserActivities = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\System' -Name 'UploadUserActivities'
LetAppsRunInBackground = Get-DwordValue -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\AppPrivacy' -Name 'LetAppsRunInBackground'
DisableTailoredExperiences_CurrentUser = Get-DwordValue -Path 'HKCU:\SOFTWARE\Policies\Microsoft\Windows\CloudContent' -Name 'DisableTailoredExperiencesWithDiagnosticData'
OnlineSpeechPrivacyHasAccepted_CurrentUser = Get-DwordValue -Path 'HKCU:\SOFTWARE\Microsoft\Speech_OneCore\Settings\OnlineSpeechPrivacy' -Name 'HasAccepted'
DiagTrack_StartMode = Get-ServiceStartMode -Name 'DiagTrack'
DmwAppPushService_StartMode = Get-ServiceStartMode -Name 'dmwappushservice'
}
foreach ($entry in $summary.GetEnumerator()) {
Write-Log (' {0} = {1}' -f $entry.Key, $entry.Value)
}
Write-Log 'Done. A reboot is recommended.'
} catch {
Write-Log "Fatal error: $($_.Exception.Message)" 'ERROR'
throw
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment