Detect Pending Windows Updates Locally with PowerShell · CloudEngineerHub
Detection & Remediation9 min read4 views
Detecting Pending Windows Updates Locally with PowerShell
Use the Microsoft.Update.Session COM API in a SYSTEM-context detection script to count pending updates per device, grouped by category, with runtime tuning notes.
C
CloudEngineerHub Team
Published: September 12, 2026· Last Updated: September 28, 2026·9 min read
Update rings and Windows Update for Business reports tell you what a deployment delivered, but they answer the question from the service side. When an admin asks "is this device actually missing updates?", the authoritative answer sits on the device itself, inside the Windows Update agent. The agent exposes that answer through a documented COM interface, and a SYSTEM-context PowerShell script can query it without installing anything. That gives you a compliance signal that reflects what the device can actually see and install, including the updates that never made it through your rings.
Visual Walkthrough
The engine behind every “Detect with PowerShell” runbook on this site.
Exit 1 flips the device into the remediation queue for this package.
remediate
3
Remediation executes
Fix script runs silently as SYSTEM with a 5-minute budget.
verify
4
Re-run detection
The same detection script must now exit 0 — proof, not hope.
telemetry
5
Results report
Per-device outcome lands in Reports → Remediations for trend analysis.
6
Repeat on schedule
The loop continues — drift gets caught the day it appears.
Continuous cycle
Proactive Remediations — The Detect → Fix → Verify Loop. 1. Detection script runs: Scheduled (daily/hourly): exit 0 healthy, exit 1 unhealthy — nothing more. — 2. Verdict: unhealthy: Exit 1 flips the device into the remediation queue for this package. — 3. Remediation executes: Fix script runs silently as SYSTEM with a 5-minute budget. — 4. Re-run detection: The same detection script must now exit 0 — proof, not hope. — 5. Results report: Per-device outcome lands in Reports → Remediations for trend analysis. — 6. Repeat on schedule: The loop continues — drift gets caught the day it appears.
C
CloudEngineerHub Team
Author
The CloudEngineerHub editorial team — Microsoft endpoint management and cloud engineers writing hands-on guides on Intune, Azure, Entra ID, Autopilot, Defender and PowerShell. Every article is reviewed against current Microsoft documentation.
This article builds that query as an Intune detection script. It counts the software updates the device considers not yet installed, groups them by update category, and logs the top titles so a support engineer reading the log on any device knows exactly what is outstanding. It is deliberately detection-only: deciding which updates to approve and when is the job of your update rings, not of a script running as SYSTEM. The one quirk to plan around is runtime: the search step talks to the update service and can take from a few seconds to two minutes.
Symptoms
The complaint rarely arrives as "updates are pending". It arrives as one of these:
A device shows compliant or current in one report but a user sees a restart-pending banner or a red update indicator in Settings.
Security audits list the device as missing a specific KB that your ring reports as deployed.
After a ring deadline passes, some devices still report "updates available" locally, and you have no per-device list of what they are.
You need to prove, per device, what outstanding updates exist during an incident or a customer audit, and the only evidence you have is a portal column that says "not compliant".
Possible Causes
Ring deferral still in effect
Deferral periods and maintenance windows mean a device can be fully healthy and still legitimately hold pending updates. Pending is not the same as broken; interpret the count against your ring schedule.
The update agent cannot reach its service
Offline devices, devices behind captive portals, and devices pointed at an unreachable WSUS server fail the search. The COM call throws, which the script reports distinctly so you do not confuse "cannot check" with "up to date".
WSUS-approved catalog differs from the service
On WSUS-managed devices the agent searches against the WSUS approvals. A device can show many pending updates that the service would auto-approve, purely because WSUS approval has not caught up.
Installs blocked by prerequisites
Some updates list as not installed because a dependent update or a pending reboot gates them. The count stays flat across scans until the gate clears.
Agent-side staleness
A corrupted datastore or a stale USO state can make the agent report offers inconsistently. If the pending list never changes across online scans, the device needs the component-level attention covered elsewhere in this series.
Detection Script
The script below is read-only and runs in 64-bit SYSTEM context. It creates a Microsoft.Update.Session COM object, runs an online search for software updates that are not installed, and logs the count, per-category totals, and up to five titles per category. Budget one to two minutes for the search step on slower devices.
PowerShell
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#Requires -RunAsAdministrator
[CmdletBinding()]
param()
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
<#
.SYNOPSIS
Detection script: counts pending software updates by querying the local
Windows Update agent through the Microsoft.Update.Session COM API.
.NOTES
Intended for Intune Scripts and Remediations, 64-bit SYSTEM context.
Read-only: the script never approves, downloads or installs anything.
Exit 0 = no pending software updates, 1 = pending updates found (count,
categories and titles in the log), 2 = the search could not complete
(device offline, WSUS unreachable, update service unhealthy), 3 =
unexpected error. The search step can legitimately take 30-120 seconds.
#>$logDir = 'C:\ProgramData\Company\Logs'$logFile = Join-Path $logDir'PendingUpdateDetection.log'$maxTitlesPerCategory = 5function Write-DetectionLog {
param([Parameter(Mandatory)][string] $Level, [Parameter(Mandatory)][string] $Message)
if (-not (Test-Path $logDir)) { New-Item -Path$logDir -ItemType Directory -Force | Out-Null }
"$([DateTime]::UtcNow.ToString('o')) [$Level] $Message" | Out-File -FilePath$logFile -Append -Encoding utf8
}
$stage = 'init'try {
Write-DetectionLog 'INFO''Pending update detection run started.'$stage = 'create-session'$session = New-Object -ComObject Microsoft.Update.Session
$searcher = $session.CreateUpdateSearcher()
$searcher.Online = $true$criteria = "IsInstalled=0 and Type='Software'"$stage = 'search'
Write-DetectionLog 'INFO'"Update search started (criteria: $criteria). This step can take 30-120 seconds."$searchResult = $searcher.Search($criteria)
$stage = 'evaluate'
Write-DetectionLog 'INFO''Update search completed.'$updates = $searchResult.Updates
$pendingCount = $updates.Count
Write-DetectionLog 'INFO'"Pending software updates: $pendingCount"if ($pendingCount -eq0) {
Write-DetectionLog 'INFO''Device is up to date for software updates.'
exit 0
}
$categoryCounts = @{}
$categoryTitles = @{}
foreach ($updatein$updates) {
$category = 'Uncategorized'foreach ($catin$update.Categories) {
$category = $cat.Name
break
}
if (-not $categoryCounts.ContainsKey($category)) {
$categoryCounts[$category] = 0$categoryTitles[$category] = @()
}
$categoryCounts[$category]++
if ($categoryTitles[$category].Count -lt$maxTitlesPerCategory) {
$categoryTitles[$category] += $update.Title
}
}
foreach ($categoryin ($categoryCounts.Keys | Sort-Object)) {
Write-DetectionLog 'INFO'"Category '$category': $($categoryCounts[$category]) pending."foreach ($titlein$categoryTitles[$category]) {
Write-DetectionLog 'INFO'" - $title"
}
}
Write-DetectionLog 'WARN'"Device is non-compliant for updates: $pendingCount software update(s) pending."
exit 1
}
catch {
if ($stage -eq'search') {
Write-DetectionLog 'ERROR'"Update search failed (device offline, WSUS unreachable, or update service unhealthy): $($_.Exception.Message)"
exit 2
}
Write-DetectionLog 'ERROR'"Unexpected failure: $($_.Exception.Message)"
exit 3
}
Detection Logic
Session and searcher. The script creates the COM session with New-Object -ComObject Microsoft.Update.Session and gets a searcher via CreateUpdateSearcher(). Setting the searcher's Online property to $true makes the intent explicit: this is a live search against the update service the device is configured to use, whether that is Microsoft's service or your WSUS server.
Search criteria. The query string IsInstalled=0 and Type='Software' selects updates the agent considers not installed, restricted to software updates. Drivers are excluded by design; add a second search with Type='Driver' if you want them. The WUA query language also supports predicates such as IsHidden and IsDownloaded for narrower result sets.
The search call.$searcher.Search($criteria) is synchronous and is where the 30 to 120 seconds go. The script logs before and after, so the log itself shows search duration per device. If the call throws, the script exits with code 2: the device could not be checked, which is a materially different state from "no updates pending".
Count and categorization. For each returned update, the script takes its first category name (for example Security Updates, Definition Updates, Updates) and accumulates counts and the first five titles per category. Every line lands in C:\ProgramData\Company\Logs\PendingUpdateDetection.log.
Verdict. Zero pending exits 0; anything else exits 1 with the count in the final log line, so the last line of the log doubles as a summary for reporting scripts that tail the file.
One honesty note: a non-zero exit marks the device as "remediation needed" in Intune even though this package has no remediation script. If you deploy this purely for reporting, treat exit 1 as informational in your reviews and expect Intune to count those devices in the "remediation needed" column.
Remediation Script
There is no remediation script for this package, and that is deliberate. The correct "fix" for pending updates is your update ring approving and scheduling the install, or the device finishing the install it already started. A SYSTEM script that force-installs updates would bypass deferral policies, fight active ring deployments, and surprise users with reboots. When detection flags a device, the remediation path is human: check the outstanding titles in the log, confirm the ring should have delivered them, and drive the install through your update deployment process.
Deploying Through Intune
Deploy as a detection-only package:
In the Microsoft Intune admin center, go to Devices → Scripts and remediations and select + Add under Windows 10/11.
On Basics, name it, for example Detect - Pending Windows updates, and note in the description that the script performs an online update search.
On Detection script, upload detect.ps1. Set Run script in 64-bit PowerShell: Yes and Run this script using signed-on credentials: No. Leave Enforce script signature check: No unless you sign internal scripts.
On the Remediation script step, choose No remediation script. This package only measures.
On Schedule, pick Weekly. Every run performs an online search; on large fleets that is meaningful traffic against your update source.
On Assignments, target a representative pilot group first, then your broad device group once you know the typical search duration.
Review and create.
Post-specific notes:
Intune enforces a script execution timeout. Test worst-case search timing on the slowest pilot device; if a device risks the timeout, schedule it less often or narrow the criteria.
WSUS-managed devices search their WSUS source. Review results per update source, not fleet-wide, or the numbers will confuse everyone.
The category grouping uses each update's first category. Updates can carry several categories; if you need exact multi-category accounting, do it in your reporting layer from the log lines.
Visual Walkthrough
Why the order of servicing stack and cumulative updates matters.
Offline servicing completes; Windows reports the new revision.
Windows Servicing Stack — SSU + LCU Anatomy. 1. SSU first: Servicing Stack Update upgrades the updater itself. — 2. LCU delivered: Latest Cumulative Update rides on top of a healthy SSU. — 3. CBS transaction: Component-Based Servicing stages payloads transactionally. — 4. Pending reboot markers: Registry markers hold the state until restart. — 5. Finalize on reboot: Offline servicing completes; Windows reports the new revision.
Exit Codes
Code
Meaning
0
No pending software updates — the device is current per its own update agent
1
One or more pending software updates found; count, categories, and titles are in the log
2
The search could not complete: device offline, WSUS unreachable, or the update service is unhealthy — the state is unknown, not healthy
3
Unexpected script error — inspect the log
Logging
The script writes to C:\ProgramData\Company\Logs\PendingUpdateDetection.log. Each line has a UTC timestamp, a level (INFO, WARN, ERROR), and a message: the criteria, the search duration window (start and completion lines), the total count, one line per category, and up to five titles per category. The final line always states the verdict and count, which makes tailing the log easy for downstream reporting. Files grow by roughly 10-20 lines per run on a device with pending updates; rotate monthly if you need a hard cap.
Troubleshooting the Script
Exit code 2 across a whole site. That site's devices cannot reach their update source. Check proxy and firewall paths to the update service, or the WSUS server's health if the site is WSUS-managed.
Searches take minutes everywhere. Expect longer searches on devices with stale datastores or on first run after provisioning. If pilot timings threaten Intune's script timeout, reduce frequency or narrow the criteria.
Count seems too high. The criteria include every not-installed software update, including deferred ones. Read the titles before escalating; the script reports what is pending, not what is overdue.
Count seems too low. Deferral-deployed updates can be absent from the agent's offer list until approval reaches the device. Compare against your ring's approval state rather than assuming the agent is wrong.
No log file appears. The script never ran: verify the assignment, the device's last check-in, and that another policy did not pin the run to user credentials that never sign in.
COM error mentioning the update service. Restart the wuauserv service on the device and re-run; persistent failures point at agent health, which the service-problems post in this series covers.
How to Validate the Fix
Pick a device, note its pending count from Settings → Windows Update, then run the script manually in an elevated 64-bit PowerShell console. The logged count should match what the UI offers.
Run it on a fully updated device. Expect exit code 0 and a single summary line.
Run it with the network path to the update service blocked (lab condition). Expect exit code 2, proving you can distinguish "unknown" from "compliant".
In the admin center, open the package's Device status. Devices should split into the code 0 and code 1 populations after their first scheduled run.
Reconcile a sample of five devices against their logs; the last log line per device is your source of truth.
Production Considerations
Execution context. SYSTEM in 64-bit Windows PowerShell. The COM session runs machine-wide; no user token is involved and no per-user data is read.
Idempotency. The script is pure read: two consecutive runs produce two log entries and no other change. There is nothing to un-do.
Side effects. The online search generates traffic to your update source and can make the USO refresh its state, but it never downloads or installs anything.
Runtime risk. The search is the only long-running part and it scales with device state and network. Validate worst-case timing in a pilot ring before fleet assignment, and keep the frequency modest.
Security review and test ring. No credentials or secrets appear in the script, and it changes nothing. Even so, have your PowerShell automation SME and security team review it and validate it in a test ring with your own devices and update infrastructure before broad production rollout.
How is this different from Intune's update reports?
Update reports show what your deployments delivered from the service side. This script asks the device's own update agent what it still considers not installed, which catches devices that fell out of ring scope, have agent-side problems, or receive updates from a different source. The two views together are what you want during audits.
Why does the search take 30 to 120 seconds?
The search is a live query against the update service, and the agent reconciles its local datastore with the offered catalog. Devices with large outstanding offer lists or slow links take longer. The script logs the start and completion so you can build a per-device duration picture from the logs and tune your schedule accordingly.
Can I include driver updates in the count?
Yes, by running a second search with Type='Driver' in the criteria. The driver catalog is typically larger and slower to search, which is why this script excludes drivers. If driver compliance matters, deploy a second detection-only package with the driver criteria rather than doubling this one's runtime.
Comments
0No comments yet — be the first to share your thoughts.