This page is used for support with plugins, it is not referenced anywhere on this site, only on outside forums.
Code Block Pro
A block from this plugin has been placed below.
Block settings are default, except for the following.

XML
# =====================================================================
# Windows Update Telemetry Detection
# Intune Detection-Only Script
# =====================================================================
$OutputFolder = "C:\ProgramData\Remediations\WindowsUpdate"
$OutputFile = Join-Path $OutputFolder "UpdateHealth.json"
function Convert-DateSafe {
param($Date)
if ($null -eq $Date) { return $null }
try {
return ([datetime]$Date).ToString("yyyy-MM-dd HH:mm:ss")
}
catch {
return $null
}
}
function Get-PendingReboot {
$pending = $false
if (Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending') {
$pending = $true
}
if (Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired') {
$pending = $true
}
try {
$sessionManager = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager' -ErrorAction Stop
if ($sessionManager.PendingFileRenameOperations) {
$pending = $true
}
}
catch {}
return $pending
}
function Get-LastInstalledUpdate {
try {
Get-HotFix |
Where-Object { $_.InstalledOn } |
Sort-Object InstalledOn -Descending |
Select-Object -First 1
}
catch {
return $null
}
}
function Get-RecentInstalledUpdates {
param([int]$Count = 10)
try {
Get-HotFix |
Where-Object { $_.InstalledOn } |
Sort-Object InstalledOn -Descending |
Select-Object -First $Count |
ForEach-Object {
[PSCustomObject]@{
HotFixID = $_.HotFixID
Description = $_.Description
InstalledOn = Convert-DateSafe $_.InstalledOn
InstalledBy = $_.InstalledBy
}
}
}
catch {
return @()
}
}
function Get-WUFailureDetails {
param([string]$Message)
$ErrorCode = $null
$KB = $null
$UpdateName = $null
if ($Message -match 'error (0x[0-9A-Fa-f]+)') {
$ErrorCode = $matches[1]
}
if ($Message -match '(KB\d{7})') {
$KB = $matches[1]
}
if ($Message -match 'error .*?: (.*)$') {
$UpdateName = $matches[1]
}
[PSCustomObject]@{
ErrorCode = $ErrorCode
KB = $KB
UpdateName = $UpdateName
}
}
function Get-RecentWindowsUpdateFailures {
param([int]$Count = 10)
try {
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'Microsoft-Windows-WindowsUpdateClient'
Level = 2
} -MaxEvents 100 -ErrorAction Stop |
Where-Object {
$_.Message -notmatch 'MICROSOFT\.WINDOWSSTORE|WindowsStore|Spotify|DesktopAppInstaller|9WZDNCRFJBMP|9NCBCSZSJRSB|9NBLGGH4NNS1'
} |
Select-Object -First $Count |
ForEach-Object {
$details = Get-WUFailureDetails $_.Message
[PSCustomObject]@{
TimeCreated = Convert-DateSafe $_.TimeCreated
EventId = $_.Id
ErrorCode = $details.ErrorCode
KB = $details.KB
UpdateName = $details.UpdateName
Message = $_.Message
}
}
}
catch {
return @()
}
}
function Get-RecentStoreUpdateFailures {
param([int]$Count = 10)
try {
Get-WinEvent -FilterHashtable @{
LogName = 'System'
ProviderName = 'Microsoft-Windows-WindowsUpdateClient'
Level = 2
} -MaxEvents 100 -ErrorAction Stop |
Where-Object {
$_.Message -match 'MICROSOFT\.WINDOWSSTORE|WindowsStore|Spotify|DesktopAppInstaller|9WZDNCRFJBMP|9NCBCSZSJRSB|9NBLGGH4NNS1'
} |
Select-Object -First $Count |
ForEach-Object {
[PSCustomObject]@{
TimeCreated = Convert-DateSafe $_.TimeCreated
EventId = $_.Id
Message = $_.Message
}
}
}
catch {
return @()
}
}
function Test-NetworkHealth {
$tests = @(
@{ Name = "Microsoft Update"; Host = "fe2.update.microsoft.com"; Port = 443 },
@{ Name = "Windows Update"; Host = "sls.update.microsoft.com"; Port = 443 },
@{ Name = "Delivery Optimization"; Host = "dl.delivery.mp.microsoft.com"; Port = 443 },
@{ Name = "Microsoft Store"; Host = "storeedgefd.dsx.mp.microsoft.com"; Port = 443 }
)
foreach ($test in $tests) {
try {
$tcp = Test-NetConnection `
-ComputerName $test.Host `
-Port $test.Port `
-InformationLevel Quiet `
-WarningAction SilentlyContinue
[PSCustomObject]@{
Name = $test.Name
Host = $test.Host
Port = $test.Port
TcpPassed = $tcp
}
}
catch {
[PSCustomObject]@{
Name = $test.Name
Host = $test.Host
Port = $test.Port
TcpPassed = $false
}
}
}
}
function Get-LastRebootReason {
try {
$event = Get-WinEvent -FilterHashtable @{
LogName = 'System'
Id = 1074
} -MaxEvents 1 -ErrorAction Stop
if ($null -eq $event) {
return $null
}
return [PSCustomObject]@{
TimeCreated = Convert-DateSafe $event.TimeCreated
Message = $event.Message
}
}
catch {
return $null
}
}
function Get-NetworkAdapterHealth {
try {
Get-NetAdapter |
Where-Object {
$_.Status -eq "Up" -and
$_.HardwareInterface -eq $true
} |
ForEach-Object {
[PSCustomObject]@{
Name = $_.Name
InterfaceDesc = $_.InterfaceDescription
Status = $_.Status.ToString()
LinkSpeed = $_.LinkSpeed
MacAddress = $_.MacAddress
DriverVersion = $_.DriverVersion
DriverDate = Convert-DateSafe $_.DriverDate
MediaConnectionState = $_.MediaConnectionState.ToString()
}
}
}
catch {
return @()
}
}
function Get-HealthSummary {
param($Result)
$reason = "Healthy"
$evidence = "No major issue detected"
$state = "Healthy"
if ($Result.CFreeGB -lt 15) {
$reason = "Low disk space"
$evidence = "CFreeGB=$($Result.CFreeGB)"
$state = "Issue"
}
elseif ($Result.NetworkHealth | Where-Object { $_.TcpPassed -eq $false }) {
$failed = $Result.NetworkHealth | Where-Object { $_.TcpPassed -eq $false } | Select-Object -First 1
$reason = "Microsoft update endpoint connectivity failure"
$evidence = "$($failed.Name) $($failed.Host):$($failed.Port) TcpPassed=False"
$state = "Issue"
}
elseif ($Result.WUServiceStatus -eq "Disabled" -or $Result.BITSServiceStatus -eq "Disabled") {
$reason = "Update service disabled"
$evidence = "WUService=$($Result.WUServiceStatus), BITS=$($Result.BITSServiceStatus)"
$state = "Issue"
}
elseif ($Result.RecentWUFailures.Count -gt 0) {
$failure = $Result.RecentWUFailures | Select-Object -First 1
$reason = "Windows Update failure events detected"
$evidence = "EventId=$($failure.EventId), Time=$($failure.TimeCreated), Error=$($failure.ErrorCode), KB=$($failure.KB), Update=$($failure.UpdateName)"
$state = "Issue"
}
elseif ($Result.PendingWUReboot -eq $true) {
$reason = "Pending reboot"
$evidence = "PendingWUReboot=True, LastBootTime=$($Result.LastBootTime)"
$state = "Issue"
}
elseif ($Result.RecentStoreFailures.Count -gt 0) {
$failure = $Result.RecentStoreFailures | Select-Object -First 1
$reason = "Store app update failures only"
$evidence = "EventId=$($failure.EventId), Time=$($failure.TimeCreated), Message=$($failure.Message)"
$state = "Warning"
}
elseif ($Result.RecentInstalledUpdates.Count -eq 0) {
$reason = "No recent installed updates found"
$evidence = "RecentInstalledUpdates=0"
$state = "Issue"
}
else {
$reason = "Healthy"
$evidence = "LastHotfix=$($Result.LastInstalledHotfix), LastHotfixDate=$($Result.LastHotfixDate)"
$state = "Healthy"
}
[PSCustomObject]@{
HealthState = $state
LikelyReason = $reason
Evidence = $evidence
}
}
function Write-DetectionOutput {
param(
[object]$Data
)
$primaryNic = $null
if ($Data -and $Data.NetworkAdapters) {
$primaryNic = $Data.NetworkAdapters |
Where-Object { $_.Status -eq "Up" -and $_.MediaConnectionState -eq "Connected" } |
Select-Object -First 1
}
$RecentFailures = @()
if ($Data -and $Data.RecentWUFailures -and $Data.RecentWUFailures.Count -gt 0) {
$RecentFailures = $Data.RecentWUFailures | Select-Object -First 3
}
$output = [PSCustomObject]@{
ComputerName = $Data.ComputerName
HealthState = $Data.HealthState
LikelyReason = $Data.LikelyReason
Evidence = $Data.Evidence
LastRebootTime = if ($Data.LastRebootReason) { $Data.LastRebootReason.TimeCreated } else { $null }
LastRebootReason = if ($Data.LastRebootReason) { $Data.LastRebootReason.Message } else { $null }
RecentWUFailures = if ($RecentFailures.Count -gt 0) {
($RecentFailures | ForEach-Object {
"$($_.TimeCreated) | $($_.ErrorCode) | $($_.KB) | $($_.UpdateName)"
}) -join " || "
} else {
$null
}
PendingWUReboot = $Data.PendingWUReboot
LastHotfix = $Data.LastInstalledHotfix
LastHotfixDate = $Data.LastHotfixDate
CFreeGB = $Data.CFreeGB
WUServiceStatus = $Data.WUServiceStatus
BITSServiceStatus = $Data.BITSServiceStatus
PrimaryNic = if ($primaryNic) { $primaryNic.Name } else { $null }
PrimaryNicSpeed = if ($primaryNic) { $primaryNic.LinkSpeed } else { $null }
CollectedAt = $Data.CollectedAt
}
$output | ConvertTo-Json -Compress
}
try {
New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
}
catch {
$fallback = [PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
HealthState = "Issue"
LikelyReason = "Failed to create output folder"
Evidence = $_.Exception.Message
}
$fallback | ConvertTo-Json -Compress
exit 1
}
$ComputerName = $env:COMPUTERNAME
$CollectedAt = Get-Date
try {
$OS = Get-CimInstance Win32_OperatingSystem -ErrorAction Stop
$BootTime = $OS.LastBootUpTime
$UptimeDays = [math]::Round(((Get-Date) - $BootTime).TotalDays, 2)
}
catch {
$OS = $null
$BootTime = $null
$UptimeDays = $null
}
try {
$Disk = Get-CimInstance Win32_LogicalDisk -Filter "DeviceID='C:'" -ErrorAction Stop
$FreeSpaceGB = [math]::Round($Disk.FreeSpace / 1GB, 2)
$TotalSpaceGB = [math]::Round($Disk.Size / 1GB, 2)
}
catch {
$FreeSpaceGB = $null
$TotalSpaceGB = $null
}
$LastHotfix = Get-LastInstalledUpdate
$RecentInstalledUpdates = @(Get-RecentInstalledUpdates -Count 10)
$RecentWUFailures = @(Get-RecentWindowsUpdateFailures -Count 10)
$RecentStoreFailures = @(Get-RecentStoreUpdateFailures -Count 10)
$PendingReboot = Get-PendingReboot
$WUService = Get-Service wuauserv -ErrorAction SilentlyContinue
$BITSService = Get-Service BITS -ErrorAction SilentlyContinue
$WSUSPolicyPresent = Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate'
$NetworkHealth = @(Test-NetworkHealth)
$NetworkAdapters = @(Get-NetworkAdapterHealth)
$LastRebootReason = Get-LastRebootReason
$Result = [PSCustomObject]@{
ComputerName = $ComputerName
CollectedAt = Convert-DateSafe $CollectedAt
OSName = if ($OS) { $OS.Caption } else { $null }
OSVersion = if ($OS) { $OS.Version } else { $null }
BuildNumber = if ($OS) { $OS.BuildNumber } else { $null }
LastBootTime = Convert-DateSafe $BootTime
UptimeDays = $UptimeDays
LastRebootReason = $LastRebootReason
LastInstalledHotfix = if ($LastHotfix) { $LastHotfix.HotFixID } else { $null }
LastHotfixDate = if ($LastHotfix) { Convert-DateSafe $LastHotfix.InstalledOn } else { $null }
RecentInstalledUpdates = $RecentInstalledUpdates
RecentWUFailures = $RecentWUFailures
RecentStoreFailures = $RecentStoreFailures
PendingWUReboot = $PendingReboot
WUServiceStatus = if ($WUService) { $WUService.Status.ToString() } else { $null }
WUServiceStartType = if ($WUService) { $WUService.StartType.ToString() } else { $null }
BITSServiceStatus = if ($BITSService) { $BITSService.Status.ToString() } else { $null }
BITSServiceStartType = if ($BITSService) { $BITSService.StartType.ToString() } else { $null }
WSUSPolicyPresent = $WSUSPolicyPresent
NetworkHealth = $NetworkHealth
NetworkAdapters = $NetworkAdapters
CFreeGB = $FreeSpaceGB
CSizeGB = $TotalSpaceGB
}
$Health = Get-HealthSummary -Result $Result
$Result | Add-Member -NotePropertyName HealthState -NotePropertyValue $Health.HealthState -Force
$Result | Add-Member -NotePropertyName LikelyReason -NotePropertyValue $Health.LikelyReason -Force
$Result | Add-Member -NotePropertyName Evidence -NotePropertyValue $Health.Evidence -Force
try {
$Result |
ConvertTo-Json -Depth 10 |
Out-File -FilePath $OutputFile -Encoding UTF8 -Force
}
catch {
$Result | Add-Member -NotePropertyName HealthState -NotePropertyValue "Issue" -Force
$Result | Add-Member -NotePropertyName LikelyReason -NotePropertyValue "Failed to write telemetry JSON" -Force
$Result | Add-Member -NotePropertyName Evidence -NotePropertyValue $_.Exception.Message -Force
Write-DetectionOutput -Data $Result
exit 1
}
Write-DetectionOutput -Data $Result
if ($Result.HealthState -eq "Issue") {
exit 1
}
exit 0Expand