Most of the Windows 10 telemetry settings rely on registry settings, services, and scheduled tasks. The PowerShell script introduced in this post allows you disable Windows 10 telemetry.

It is important to note that that this script does not remove all Windows 10 telemetry. However, you can use the script to add more registry keys and disable services or scheduled tasks related to telemetry.

Privacy Options in Windows 10

Privacy Options in Windows 10

Function ChangeReg {
  param ([string] $RegKey,
         [string] $Value,
         [string] $SvcName,
         [Int] $CheckValue,
         [Int] $SetData)
  Write-Host "Checking if $SvcName is enabled" -ForegroundColor Green
  if (!(Test-Path $RegKey)){
      Write-Host "Registry Key for service $SvcName does not exist, creating it now" -ForegroundColor Yellow
      New-Item -Path (Split-Path $RegKey) -Name (Split-Path $RegKey -Leaf) 
     }
 $ErrorActionPreference = 'Stop'
 try{
      Get-ItemProperty -Path $RegKey -Name $Value 
      if((Get-ItemProperty -Path $RegKey -Name $Value).$Value -eq $CheckValue) {
          Write-Host "$SvcName is enabled, disabling it now" -ForegroundColor Green
          Set-ItemProperty -Path $RegKey -Name $Value -Value $SetData -Force
         }
      if((Get-ItemProperty -Path $RegKey -Name $Value).$Value -eq $SetData){
             Write-Host "$SvcName is disabled" -ForegroundColor Green
         }
     } catch [System.Management.Automation.PSArgumentException] {
       Write-Host "Registry entry for service $SvcName doesn't exist, creating and setting to disable now" -ForegroundColor Yellow
       New-ItemProperty -Path $RegKey -Name $Value -Value $SetData -Force
      }
   }
  
 # Disabling Advertising ID
 $RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo"
 $Value = "Enabled"
 $SvcName = "Advertising ID"
 $CheckValue = 1
 $SetData = 0
 ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData
 #Telemetry Disable
 $RegKey = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection"
 $Value = "AllowTelemetry"
 $SvcName = "Telemetry"
 $CheckValue = 1
 $SetData = 0        
 ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData        
 #SmartScreen Disable
 $RegKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppHost\EnableWebContentEvaluation"
 $Value = "Enabled"
 $SvcName = "Smart Screen"
 $CheckValue = 1
 $SetData = 0
 ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData
 Write-Host "Disabling DiagTrack Services" -ForegroundColor Green 
 Get-Service -Name DiagTrack | Set-Service -StartupType Disabled | Stop-Service
 Get-Service -Name dmwappushservice | Set-Service -StartupType Disabled | Stop-Service
 Write-Host "DiagTrack Services are disabled" -ForegroundColor Green 
 Write-Host "Disabling telemetry scheduled tasks" -ForegroundColor Green
 $tasks ="SmartScreenSpecific","ProgramDataUpdater","Microsoft Compatibility Appraiser","AitAgent","Proxy","Consolidator",
         "KernelCeipTask","BthSQM","CreateObjectTask","Microsoft-Windows-DiskDiagnosticDataCollector","WinSAT",
         "GatherNetworkInfo","FamilySafetyMonitor","FamilySafetyRefresh","SQM data sender","OfficeTelemetryAgentFallBack",
         "OfficeTelemetryAgentLogOn"
 $ErrorActionPreference = 'Stop'
 $tasks | %{
    try{
       Get-ScheduledTask -TaskName $_ | Disable-ScheduledTask
       } catch [Microsoft.PowerShell.Cmdletization.Cim.CimJobException] { 
    "task $($_.TargetObject) is not found"
    }
 }

Now I’ll go line by line through this script.

Function ChangeReg {

 param ([string] $RegKey,
         [string] $Value,
         [string] $SvcName,
         [Int] $CheckValue,
         [Int] $SetData)

Because more than one registry key needs modification, I decided to write this part as a function. The function accepts the following parameters:

$RegKey – represents the registry

$Value –name of the registry entry

$SvcName – name of the service

$CheckValue – initial value of the registry entry

$SetData – desired value of the registry entry

Write-Host "Checking if $SvcName is enabled" -ForegroundColor Green
  if (!(Test-Path $RegKey)){
      Write-Host "Registry Key for service $SvcName does not exist, creating it now" -ForegroundColor Yellow
      New-Item -Path (Split-Path $RegKey) -Name (Split-Path $RegKey -Leaf) 
     }

This section informs the user what the script is currently doing using the Write-Host cmdlet. Then it checks if the registry key that we have to change exists using Test-Path. If not, I create this key, spitting the original $RegKey value into the parent and leaf with the help of the Split-Path cmdlet. I’m doing this while bearing in mind that a lot of Windows services are enabled by default but don't have registry keys or values for disabling them. This seems to be true for some telemetry services as well.

$ErrorActionPreference = 'Stop'
 try{
      Get-ItemProperty -Path $RegKey -Name $Value 
      if((Get-ItemProperty -Path $RegKey -Name $Value).$Value -eq $CheckValue) {
          Write-Host "$SvcName is enabled, disabling it now" -ForegroundColor Green
          Set-ItemProperty -Path $RegKey -Name $Value -Value $SetData -Force
         }
      if((Get-ItemProperty -Path $RegKey -Name $Value).$Value -eq $SetData){
             Write-Host "$SvcName is disabled" -ForegroundColor Green
         }
     } catch [System.Management.Automation.PSArgumentException] {
       Write-Host "Registry entry for service $SvcName doesn't exist, creating and setting to disable now" -ForegroundColor Yellow
       New-ItemProperty -Path $RegKey -Name $Value -Value $SetData -Force
      }
   }
      }

Now I need to check whether the registry entry that is responsible for disabling a particular telemetry option already exists. I did this using a try-catch block to avoid errors that the Get-ItemProperty cmdlet would generate if the item I’m trying to read does not exist. To accomplish this, I have to set $ErrorActionPreference to 'Stop' before the try clause because try-catch only reacts to fatal errors. [System.Management.Automation.PSArgumentException] is not one of them. Thus, I ensure that any error is considered fatal using the $ErrorActionPreference variable.

If the registry entry exists, I use an if-statement to check whether the value of this entry equals one, which means that the corresponding telemetry service is enabled. In this case, I let the user know that this particular telemetry option is enabled and that it is going to be disabled. Then I set the registry entry value to $SetData, which disables the telemetry option.

The second if-statement verifies whether the value was changed successfully and then informs the user that it was. The catch statement comes into play if there was an exception, which means that the registry entry does not exist. If this happens, I create a new entry using the New-ItemProperty cmdlet and assign the value that disables the telemetry option.

# Disabling Advertising ID
 $RegKey = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\AdvertisingInfo"
 $Value = "Enabled"
 $SvcName = "Advertising ID"
 $CheckValue = 1
 $SetData = 0
 ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData
 #Telemetry Disable
 $RegKey = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection"
 $Value = "AllowTelemetry"
 $SvcName = "Telemetry"
 $CheckValue = 1
 $SetData = 0        
 ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData        
 #SmartScreen Disable
 $RegKey = "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\AppHost\EnableWebContentEvaluation"
 $Value = "Enabled"
 $SvcName = "Smart Screen"
 $CheckValue = 1
 $SetData = 0
 ChangeReg -RegKey $RegKey -Value $Value -SvcName $SvcName -CheckValue $CheckValue -SetData $SetData

As you can see, all this bunch of code does is set the variables to the values that correspond to the most obvious telemetry components and then uses the ChangeReg function to disable them.

Write-Host "Disabling DiagTrack Services" -ForegroundColor Green 
 Get-Service -Name DiagTrack | Set-Service -StartupType Disabled | Stop-Service
 Get-Service -Name dmwappushservice | Set-Service -StartupType Disabled | Stop-Service
 Write-Host "DiagTrack Services are disabled" -ForegroundColor Green 
 Now I disabling two system services which send telemetry data to Microsoft.. 
 $tasks ="SmartScreenSpecific","ProgramDataUpdater","Microsoft Compatibility Appraiser","AitAgent","Proxy","Consolidator",
         "KernelCeipTask","BthSQM","CreateObjectTask","Microsoft-Windows-DiskDiagnosticDataCollector","WinSAT",
         "GatherNetworkInfo","FamilySafetyMonitor","FamilySafetyRefresh","SQM data sender","OfficeTelemetryAgentFallBack",
         "OfficeTelemetryAgentLogOn"

Finally, I’m coming to the scheduled tasks that are used for telemetry processes. I store them into a string array. Below is a short description of each task:

SmartScreenSpecific – collects data for Microsoft SmartScreen

ProgramDataUpdater – collects program telemetry information if opted-in to the Microsoft Customer Experience Improvement Program (CEIP)

Microsoft Compatibility Appraiser – ollects program telemetry information if opted-in to the CEIP

AitAgent – aggregates and uploads application telemetry information if opted-in to the CEIP

Proxy – collects and uploads Software Quality Management (SQM) data if opted-in to the CEIP

Consolidator – collects and sends usage data to Microsoft (if the user has consented to participate in the CEIP)

KernelCeipTask (Kernel Customer Experience Improvement Program) – collects additional information related to customer experience and sends it to Microsoft (if the user consented to participate in the Windows CEIP)

BthSQM (Bluetooth Customer Experience Improvement Program) – collects Bluetooth-related statistics and information about your machine and sends it to Microsoft (if you have consented to participate in the Windows CEIP). The information received is used to help improve the reliability, stability, and overall functionality of Bluetooth in Windows.

DiskDiagnosticDataCollector (Windows Disk Diagnostic reports) – collects general disk and system information and sends it to Microsoft (if the user users participates in the CEIP)

WinSAT – measures system performance and capabilities

GatherNetworkInfo – collects network information

FamilySafetyMonitor – initializes family safety monitoring and enforcement

FamilySafetyRefresh – synchronizes the latest settings with the family safety website

SQM data sender - sends SQM data to Microsoft

OfficeTelemetryAgentFallBack – initiates the background task for the Office Telemetry Agent that scans and uploads usage and error information for Office solutions

OfficeTelemetryAgentLogOn – initiates the Office Telemetry Agent that scans and uploads usage and error information for Office solutions when a user logs on to the computer

$ErrorActionPreference = 'Stop'
 $tasks | %{
    try{
       Get-ScheduledTask -TaskName $_ | Disable-ScheduledTask
       } 
    catch [Microsoft.PowerShell.Cmdletization.Cim.CimJobException] { 
      "task $($_.TargetObject) is not found"
    } 
 }

Some tasks might be absent on different Windows 10 installations, so I added a try-catch block to avoid error messages’ being displayed on the console. "$ErrorActionPreference  = 'Stop'" serves the same purpose as it did in the previous case; that is, it ensures that any error calls the Stop instruction.

Subscribe to 4sysops newsletter!

At the end of the script, I’m trying to disable the task from the $tasks array using the Get-ScheduledTask cmdlet. First, I get the task object, and then, if this operation is successful, I pipe this object to Disable-ScheduledTask. If there is no task with such a name, the catch section generates and catches a Microsoft.PowerShell.Cmdletization.Cim.CimJobException exception . In this case, the message that the task was not found will be produced.

avatar
12 Comments
  1. Miroslav 5 years ago

    nice!

  2. Allan 5 years ago

    Just what I was looking for.

  3. Melvin Backus 5 years ago

    Hmm, seems like there may be a problem with the try/catch on the scheduled tasks. The ones that are present appear to be getting disabled, but the ones that aren't present are generating errors.

    • Author

      I just ran it on couple test VMs with windows 1o. This is the output from one of them:

      TaskPath                                       TaskName                          State
      --------                                       --------                          -----
      MicrosoftWindowsAppID                      SmartScreenSpecific               Disabled
      MicrosoftWindowsApplication Experience     ProgramDataUpdater                Disabled
      MicrosoftWindowsApplication Experience     Microsoft Compatibility Appraiser Disabled
      task AitAgent is not found
      MicrosoftWindowsAutochk                    Proxy                             Disabled
      MicrosoftWindowsCustomer Experience Impr... Consolidator                      Disabled
      MicrosoftWindowsCustomer Experience Impr... KernelCeipTask                    Disabled
      task BthSQM is not found
      MicrosoftWindowsShell                      CreateObjectTask                  Disabled
      MicrosoftWindowsCloudExperienceHost        CreateObjectTask                  Disabled
      MicrosoftWindowsDiskDiagnostic             Microsoft-Windows-DiskDiagnost... Disabled
      MicrosoftWindowsMaintenance                WinSAT                            Disabled
      MicrosoftWindowsNetTrace                   GatherNetworkInfo                 Disabled
      MicrosoftWindowsShell                      FamilySafetyMonitor               Disabled
      MicrosoftWindowsShell                      FamilySafetyRefresh               Disabled
      task SQM data sender is not found
      task OfficeTelemetryAgentFallBack is not found
      task OfficeTelemetryAgentLogOn is not found

      However, I've seen such behavior for some scripts before. And usually when you run the try-catch block for the first time it throws an error, but second time it works as it supposed to do.  I have no logical explanation to it.

  4. psc 5 years ago

    Hi,

    Just to alert you to the typo at :

    $ErrorActionPreference = ‘'Stop

  5. Melvin Backus 5 years ago

    I tried running a few times, no change. Even after killing the PS session and starting a new one, just in case things were cached.  I'm getting the error below. Taskname changes but otherwise same stuff. There are about 5 or so in my case.

    Get-ScheduledTask : No MSFT_ScheduledTask objects found with property 'TaskName' equal to 'AitAgent'.  Verify the
    value of the property and retry.
    At C:\Users\xxxx\Kill10Telemetry.ps1:61 char:8
    +        Get-ScheduledTask -TaskName $_ | Disable-ScheduledTask
    +        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (AitAgent:String) [Get-ScheduledTask], CimJobException
    + FullyQualifiedErrorId : CmdletizationQuery_NotFound_TaskName,Get-ScheduledTask

    • Author

      Well, I just have to admit it does throw and error when ran from PowerShell console. It works perfectly when ran from PowerShell ISE, though. I can't understand why there is such a difference.

      The Set-StrictMode command doesn't change console behavior as well.

      Will continue to investigate.

       

  6. Melvin Backus 5 years ago

    Since this was just a single machine for now I decided to run it from the ISE. It failed there as well.  In case it makes any difference this is running on 10586.  Powershell version reports as 5/0/10586/494

     

    • Author

      So, the only way I could get it to work in console is adding the -ErrorAction to each command in the trycatch block. Which is weird, to be honest. Please find the peace of code below:

      $tasks | %{
              try{
              Get-ScheduledTask -TaskName $_ -ErrorAction Stop | Disable-ScheduledTask -ErrorAction Stop
              } catch{ 
          "task $($_.TargetObject) is not found"
          }
      

  7. Jon 2 years ago

    1 - when i copied this script, i got a lot of unicode spaces. don't know how that happened.

    2 - when i run it, i get lots of what looks like dumped registry entries in white text and access errors in red text.

    does this need to be updated?

  8. Jim portosin 12 months ago

    undo ?????

Leave a reply

Please enclose code in pre tags

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2021

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account