HomeNewsletterCommunityMVP FeedToolsArchiveBlogToday's NewsAboutServicesQuick Links Subscribe free
← Back to Blog
Windows Update Windows UpdateWaaSMedicSvcProtected ServicesIntuneWindows Update for BusinessPolicy CSPGroup PolicyTroubleshooting

Your Windows Update config reverted itself: WaaSMedicSvc, protected services, and the two other things that undo your changes

IA
Imran Awan
23 August 2026

An admin sets wuauserv to Disabled on a machine that keeps rebooting itself. Four hours later it is Running, start type Manual, exactly as it shipped. Nobody touched the box. There is no change record. So the admin does it again, and it happens again.

The usual next move is to go after the thing that is undoing the change. Somebody finds a forum post naming the Windows Update Medic Service, runs sc config WaaSMedicSvc start= disabled from an elevated prompt, and gets Access is denied from a session that is already SYSTEM-adjacent and holds every privilege that matters. Now there are two mysteries, and the second one sends people down a permissions rabbit hole that cannot possibly end anywhere useful.

Both mysteries have clean answers. Neither answer is a permissions problem. And in a managed fleet, the most common cause of "my update config reverted" is not the medic at all.

The short version

Three different mechanisms undo Windows Update configuration, and they leave different fingerprints. WaaSMedicSvc repairs tampered update components; it is a protected service, so Microsoft documents that unprotected callers simply cannot call ChangeServiceConfig, ControlService, DeleteService or SetServiceObjectSecurity against it — which is why re-ACLing it is pointless, not just unsupported. Policy Manager is the one people mis-blame: Microsoft documents that with MDMWinsOverGP set, "any values set by scripts/user outside of GP that conflict with MDM are removed", on every sync. And the third case is not a revert at all — Intune-delivered update policy lands under HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Update, not the classic Policies hive, so a value can look missing when it was never supposed to be there. Read the protection level with sc qprotection, read both hives, and read MDMWinsOverGP — before you change anything.

The problem: the config goes back, and the service will not let you stop it

The symptom arrives in one of three shapes, and telling them apart is the entire job.

Shape one. You changed a service state or a registry value by hand, it held for a while, and then it went back to the platform default. Something actively repaired it.

Shape two. You changed a registry value by hand or by script, and it disappeared or reverted on a rhythm that looks suspiciously like a management sync interval. Something enforced a policy over the top of you.

Shape three. You wrote a value, went back to check it, and it is not there — or you went looking for a value the update ring is supposed to have set and found nothing. Nothing reverted anything. You are reading the wrong registry hive.

These three get collapsed into one story ("WaaSMedic keeps undoing my changes") constantly, because the first one is the most famous and the other two are invisible unless you know where to look. They need completely different responses, and two of the three are not the medic's fault at all.

Then there is the second symptom, the one that turns a twenty-minute investigation into an afternoon. You try to take the medic out of the picture and the platform refuses:

elevated adminsc config WaaSMedicSvc start= disabledAccess is denied The same shell can stop wuauserv, reconfigure bits, and delete services outright. It is not short of rights.

Because the error says access denied, the next hour goes on access. People dump the service security descriptor. They check the registry key ACL. They compare against a service they can configure. Every one of those checks comes back clean, which is genuinely confusing, so the conclusion becomes "the ACL must be lying" and the next step is to take ownership and rewrite it.

Watch out: at this point two cargo-cult moves usually happen and both destroy the evidence you need. Deleting C:\Windows\SoftwareDistribution throws away the download and detection state that would have shown you what the client was actually doing, and it fixes nothing here because nothing in this failure lives there. Taking ownership of HKLM\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc and rewriting its permissions changes a security descriptor that, as measured below, was never the thing blocking you — while leaving you with a hand-modified servicing component that a future cumulative update will overwrite, and a fleet where that one machine no longer matches any baseline you can reason about.

So: diagnose first. The good news is that the whole thing resolves in about five commands, all of them read-only, and the first one settles the access-denied mystery outright.

Why it happens: a protected service, a COM-handler task, and two policy engines

What WaaSMedic actually is

Windows Update Medic Service is the update stack's self-repair component. It looks at the services, policies and scheduled tasks that Windows Update depends on, decides whether they have been tampered with or corrupted, and puts them back.

Worth being precise about the sourcing here, because it shapes how much of the rest you should trust. Microsoft does not publish a Learn article for this service. I looked, twice, including a domain-scoped search: what comes back is Microsoft Q&A threads, which are user-generated. The nearest thing to an authoritative Microsoft description of the service is a Microsoft-authored string, but it ships inside the binary rather than on a docs page:

Windows PowerShell — where the medic's files actually live (real output, 26200.9168)
# Read-only: version resources only. Nothing here modifies the service. C:\WINDOWS\System32\WaaSMedicSvc.dll EXISTS ver=10.0.26100.8737 desc='Enables remediation and protection of Windows Update components.' C:\WINDOWS\System32\WaaSMedicPS.dll EXISTS ver=10.0.26100.8737 desc='WaaS Medic Proxy Stub library' C:\WINDOWS\System32\WaaSAssessment.dll EXISTS ver=10.0.26100.8972 desc='WaaS Assessment' C:\WINDOWS\System32\WaaSMedicAgent.exe MISSING C:\WINDOWS\System32\WaaSMedicCapsule.dll MISSING # The two that are "missing" from System32 are not missing. They are in the UUS payload: C:\WINDOWS\UUS\amd64\WaaSMedicAgent.exe EXISTS ver=10.0.26100.8972 desc='WaasMedic Agent Exe' C:\WINDOWS\UUS\amd64\WaaSMedicCapsule.dll EXISTS ver=10.0.26100.8972 desc='WaasMedic Capsule Exe' C:\WINDOWS\UUS\amd64\WaaSMedicSvcImpl.dll EXISTS ver=10.0.26100.1 desc='WaasMedic Service Dll'

That first description line — "Enables remediation and protection of Windows Update components" — is the service's own FileDescription resource. Treat it as Microsoft's statement of intent, and treat everything below about how it decides to remediate as observed rather than documented, because Microsoft does not publish that logic.

The path split matters more than it looks. WaaSMedicAgent.exe is the process name people see in Task Manager and in security tooling, and it is not in System32. It lives in C:\Windows\UUS\amd64, the Unified Update Stack payload directory — the same place MoUsoCoreWorker.exe lives, and for the same reason. A detection script that tests Test-Path C:\Windows\System32\WaaSMedicAgent.exe reports the agent absent on a perfectly healthy machine.

Context: UUS is serviced independently of the OS build, which is why the version numbers in that output disagree with each other and with the OS. This device is build 26200.9168, its System32\WaaSMedicSvc.dll is 26100.8737, its UUS agent is 26100.8972, and WaaSMedicSvcImpl.dll reports 26100.1. On the same box, MoUsoCoreWorker.exe reports 1509.2607.1012.0 — the same versioning scheme as wuaueng.dll. Four different version lineages inside one update stack is normal. "The medic DLL is older than the OS" is not a finding.

The reason services.msc often cannot even name it

A small mystery that turns out to be the same mystery. The service's own registry key does not store a display name or description as literal text; it stores indirect string references, and the scheduled task does the same thing:

Windows PowerShell — the resource strings point somewhere the loader will not find
# HKLM\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc DisplayName : @WaaSMedicSvcImpl.dll,-100 Description : @WaaSMedicSvcImpl.dll,-101 # \Microsoft\Windows\WaaSMedic\PerformRemediation, Author and Description fields Author : $(@%systemroot%\system32\WaasMedicSvcImpl.dll,-102) Description : $(@%systemroot%\system32\WaasMedicSvcImpl.dll,-104) # But WaaSMedicSvcImpl.dll is in UUS\amd64, not system32. So the lookup fails # and Get-Service falls back to the raw service name: Name : WaaSMedicSvc DisplayName : WaaSMedicSvc <-- not "Windows Update Medic Service" Status : Stopped StartType : Manual

The task definition literally points at %systemroot%\system32\WaasMedicSvcImpl.dll, and that file is not there. This is the mechanism behind a symptom people report to Microsoft Q&A regularly: the service showing as Failed to Read Description. Error Code: 2 in the Services console, or the friendly name not appearing in the list at all. Error 2 is ERROR_FILE_NOT_FOUND.

I am labelling that one observed, not documented — the resource paths and the fallback behaviour are measured on this device and the symptom is widely reported, but Microsoft does not document the resource layout, so I cannot tell you it is intentional versus a packaging artefact. What matters operationally is narrower and safe to rely on: do not use the display name to find this service. Key off WaaSMedicSvc.

The actual reason you get access denied

Here is the part that ends the permissions hunt. WaaSMedicSvc is a protected service. Microsoft documents this as a service protection type, set on the service and queried through QueryServiceConfig2, with four possible values:

ValueConstantWhat it means
0SERVICE_LAUNCH_PROTECTED_NONEOrdinary service. An admin with the right DACL entry can configure, stop and delete it.
1SERVICE_LAUNCH_PROTECTED_WINDOWSDocumented as "reserved for internal Windows use only".
2SERVICE_LAUNCH_PROTECTED_WINDOWS_LIGHTAlso "reserved for internal Windows use only". This is WaaSMedicSvc.
3SERVICE_LAUNCH_PROTECTED_ANTIMALWARE_LIGHTThe one third parties may use, for anti-malware services, with an appropriate certificate.

Microsoft's documentation for that structure then states the consequence directly: once the service is launched as protected, "other unprotected processes will not be able to call the following APIs on the protected service". The list is short and it is exactly the list of things an admin trying to neutralise the medic would reach for.

Blocked APIWhat you actually typedResult from an unprotected process
ChangeServiceConfigsc config WaaSMedicSvc start= disabled, or Set-Service -StartupType DisabledAccess denied. The start type does not change.
ChangeServiceConfig2sc failure, description or delayed-start changesAccess denied.
ControlService / ControlServiceExsc stop WaaSMedicSvc, Stop-Service WaaSMedicSvcAccess denied. It cannot be stopped from user mode.
DeleteServicesc delete WaaSMedicSvcAccess denied.
SetServiceObjectSecuritysc sdset WaaSMedicSvc ...Access denied — you cannot re-permission it through the SCM either.

That last row is the one worth sitting with. The API that would let you grant yourself more access is itself on the blocked list. The "just fix the ACL" plan is not merely unsupported; through the supported interface it is unreachable.

The measurement that kills every ACL theory

And now the genuinely counter-intuitive part, which is why so many people conclude the permissions are corrupt. I read the service's security descriptor, read the registry key ACL, and then did an access check by opening a handle for each right that matters. Opening a handle changes nothing, so this is safe to run anywhere.

Windows PowerShell (elevated) — the permissions are fine, and it still fails
# 1. The service security descriptor. BA = Built-in Administrators. PS> sc.exe sdshow WaaSMedicSvc D:(A;;CCLCSWRPLORC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;SY)S:(AU;FA;CCDCLCSWRPWPDTLOSDRCWDWO;;;WD) # ^^ BA holds DC (change config), WP (stop), SD (delete), WD (write DAC) # 2. The registry key ACL. Also fine. PS> (Get-Acl 'HKLM:\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc').Access BUILTIN\Administrators Allow FullControl NT AUTHORITY\SYSTEM Allow FullControl BUILTIN\Users Allow ReadKey # 3. Access check: OpenService() for each right, from an elevated admin shell. SERVICE_QUERY_CONFIG 0x0001 GRANTED SERVICE_CHANGE_CONFIG 0x0002 GRANTED <-- the handle opens SERVICE_QUERY_STATUS 0x0004 GRANTED SERVICE_START 0x0010 GRANTED SERVICE_STOP 0x0020 GRANTED <-- this one too DELETE 0x10000 GRANTED READ_CONTROL 0x20000 GRANTED WRITE_DAC 0x40000 GRANTED # 4. And the protection level, read through the documented API: QueryServiceConfig2(SERVICE_CONFIG_LAUNCH_PROTECTED): WaaSMedicSvc dwLaunchProtected = 2 wuauserv dwLaunchProtected = 0 DoSvc dwLaunchProtected = 2

Read that again. OpenService succeeds for SERVICE_CHANGE_CONFIG, for SERVICE_STOP, for DELETE, for WRITE_DAC. The SCM hands you the handle. The DACL grants Administrators everything. The registry key grants Administrators FullControl.

The refusal happens later, when you actually call ChangeServiceConfig or ControlService on that perfectly valid handle. The protection level is enforced at call time, not at handle-open time.

Which means every permission-shaped diagnosis of this problem is a dead end by construction. sdshow looks healthy because it is healthy. The access check passes because access was never denied. Nothing is corrupt. You are hitting a different gate entirely, one that no amount of ACL work touches — and one that a single command will show you.

Tip: sc.exe qprotection WaaSMedicSvc is the five-second triage for this whole class of confusion. It prints the protection level in English and it is read-only. One caveat, in the interest of accuracy: I could not find qprotection in Microsoft's published sc.exe command reference, so treat the subcommand as observed, not documented even though the API it wraps — QueryServiceConfig2 with SERVICE_CONFIG_LAUNCH_PROTECTED — is fully documented. If you need a documented path for a compliance script, call the API.

The service key, value by value

Everything that makes this service behave the way it does is visible in one registry key, read-only. Parent key for the table below:

HKLM\SYSTEM\CurrentControlSet\Services\WaaSMedicSvc
ValueMeaningWhat to look for
LaunchProtectedThe service protection type. Maps to the SERVICE_LAUNCH_PROTECTED_* constants.2 on this device. Anything non-zero means user-mode configuration and control calls will be refused regardless of the DACL. This is the value that explains your access-denied.
StartStart type. 2 = Automatic, 3 = Manual/demand, 4 = Disabled.3 on a healthy device. If you find 4 here, someone wrote it directly into the registry rather than through the SCM — that is the unsupported route, and it is a finding about your fleet, not about Windows.
ImagePathThe host process and service group.svchost.exe -k wusvcs -p. The -p is the protected-host flag. This is how you spot the medic in Task Manager.
ObjectNameThe account the service runs as.LocalSystem.
TypeService type bitmask.32 (0x20), a shared-process Win32 service — even though wusvcs currently has exactly one member.
RequiredPrivilegesPrivileges the service asks for at start.Nine of them, including SeTcbPrivilege, SeTakeOwnershipPrivilege, SeBackupPrivilege and SeRestorePrivilege. This is a component built to overwrite things you own.
Parameters\ServiceDllThe DLL svchost loads for this service.C:\WINDOWS\System32\WaaSMedicSvc.dll. Note this one is in System32, unlike the agent executable.
DisplayName / DescriptionIndirect string references, not literal text.@WaaSMedicSvcImpl.dll,-100 and -101. Expect these to fail to resolve. Do not match on display name.

The wusvcs group is worth one line on its own. On this device HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost lists exactly one member for wusvcs: WaaSMedicSvc. So svchost.exe -k wusvcs -p in a process list is unambiguous — that is the medic, and nothing else shares the host.

For contrast, the same read across the neighbouring update services shows how unusual this is. wuauserv, UsoSvc, bits and TrustedInstaller all report no protection at all. DoSvc, Delivery Optimization, is the other one at level 2. So of the six services in the Windows Update path, two are protected and four are not — and the four unprotected ones are exactly the four you can break by hand, which is precisely why the medic exists.

The scheduled task, and why disabling it does nothing

There is one scheduled task, at a documented-looking path, and its contents surprise most people:

Windows PowerShell — Export-ScheduledTask, trimmed (real output)
# \Microsoft\Windows\WaaSMedic\PerformRemediation <Task version="1.4"> <Principals><Principal id="LocalSystem"><UserId>S-1-5-18</UserId></Principal></Principals> <Triggers> <TimeTrigger> <StartBoundary>2000-10-15T03:00:00</StartBoundary> <Interval>P7D</Interval> </TimeTrigger> </Triggers> <Actions Context="LocalSystem"> <ComHandler> <ClassId>{72566E27-1ABB-4EB3-B4F0-EB431CB1CB32}</ClassId> </ComHandler> </Actions> </Task> # Task state, and what it did last: State : Ready LastRunTime : 08/23/2026 10:33:43 LastTaskResult : 0 (0x00000000) NextRunTime : 08/30/2026 04:26:57 NumberOfMissedRuns : 0 ExecutionTimeLimit : PT72H StartWhenAvailable : True RunOnlyIfNetworkAvailable : False RunOnlyIfIdle : False DisallowStartIfOnBatteries : False

Three things there change how you think about this.

First, the action is a COM handler, not an executable. There is no command line. So there is nothing to audit, nothing to substitute, and no Execute path for a security tool to allow-list or block. Get-ScheduledTask reports the action's Execute and Arguments as empty strings, which reads like a broken task if you do not know to expect it.

Second, the settings are deliberately hard to starve. A 72-hour execution time limit, catch-up enabled, no network requirement, no idle requirement, and it will run on battery. You cannot wait it out and you cannot arrange conditions where it declines to run.

Third — and this is the one that matters for the "just disable the task" plan — the trigger is weekly, but the service runs far more often than weekly. Every start of the medic drops a trace file, and counting those files tells you the real cadence:

Windows PowerShell — C:\Windows\Logs\waasmedic (real output)
PS> Get-ChildItem C:\Windows\Logs\waasmedic -Filter *.etl | Measure-Object Count : 151 TotalMB : 3.74 # Size distribution. Each service start writes one .etl. 16 KB (empty/stub) 139 16-128 KB 10 >=128 KB (did work) 2 oldest : waasmedic.20260816_151753_847.etl newest : waasmedic.20260823_104002_237.etl count in last 24h : 30 <-- not weekly. thirty times in a day.

Thirty starts in twenty-four hours, on a device with nothing wrong with it, against a scheduled task that fires once a week. The task is one trigger among several; the rest are on-demand starts from other update components. Disabling the task removes the weekly sweep and leaves the other twenty-nine.

The size distribution is the useful diagnostic in that output. Of 151 traces, 139 are exactly 16 KB — the empty-file floor. Two are over 128 KB. So on this device the medic starts constantly, finds nothing to repair almost every time, and did substantial work exactly twice in a week.

Gotcha: there is no WaaSMedic event log. I swept every registered channel and every registered provider on this device for anything matching WaaS, Medic or Remediation, through both Get-WinEvent -ListLog/-ListProvider and logman query providers. Zero hits in all three. If you have been told to "check the WaaSMedic operational log", there is not one to check — the evidence trail is the .etl files in C:\Windows\Logs\waasmedic, and those are ETW traces, not a channel you can query with a filter hashtable. There is also no HKLM\SOFTWARE\Microsoft\WaaSMedic state key; I checked three plausible locations and none exist. Both points are observed, not documented, and both are worth knowing before you spend an hour looking for a log that was never shipped.

The two revert mechanisms that are not the medic

Now the part that actually matters in a managed fleet, because in an Intune-enrolled estate this is the more likely answer.

Microsoft documents a policy setting called MDMWinsOverGP, in the ControlPolicyConflict area of Policy CSP. Set to 1, "any MDM policy that's set that has an equivalent GP policy will result in GP service blocking the setting of the policy by GP MMC." Fine. But read the next part of that documentation, because it is describing your symptom in Microsoft's own words. The policy "should be set at every sync", and one of the three things this ensures is:

"Any values set by scripts/user outside of GP that conflict with MDM are removed."

That is documented, deliberate, per-sync removal of exactly the kind of hand-written registry value people are blaming the medic for. It reverts on the MDM sync rhythm, which on a normal client is hours — matching the "it came back four hours later" story far better than a weekly task does.

The same documentation adds a warning that explains a whole category of intermittent behaviour: the same settings "should not be configured in both GPO and MDM policies unless the settings are under the control of MDMWinsOverGP. Otherwise, there will be a race condition and no guarantee which one wins." A documented race condition is a much better explanation for "it reverts sometimes" than any self-repair story.

And then the third case, which is not a revert at all. Update policy delivered by Intune does not land in the classic Group Policy hive. It lands in the Policy Manager hive:

Intune update ringPolicy CSP ./Device/.../Update/HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Update Group Policy, by contrast, writes to HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate. Two hives, two engines. Reading one and concluding anything about the other is the single most common self-inflicted wound here.

So when someone says "the ring is applied but DeferQualityUpdatesPeriodInDays is missing from the registry", they are almost always looking in Policies\...\WindowsUpdate, where it was never going to be. Nothing reverted. The value is in the other hive, and it has been there all along.

How to verify: prove which of the three actually reverted you

Eight steps, all read-only, in this order. Every one of them is safe on a production machine and none of them destroys state you might need. Do not run Get-WindowsUpdateLog anywhere in this sequence — it stops the Update Orchestrator and Windows Update services to flush traces, which perturbs the exact thing you are measuring.

Step 1 — settle the access-denied question before anything else. If your investigation started with a refused sc config, resolve that first so you stop theorising about permissions.

cmd / PowerShell — sc qprotection across the update services (real output)
PS> foreach ($s in 'WaaSMedicSvc','wuauserv','DoSvc','UsoSvc') { sc.exe qprotection $s } [SC] QueryServiceConfig2 SUCCESS SERVICE WaaSMedicSvc PROTECTION LEVEL: WINDOWS LIGHT. [SC] QueryServiceConfig2 SUCCESS SERVICE wuauserv PROTECTION LEVEL: NONE. [SC] QueryServiceConfig2 SUCCESS SERVICE DoSvc PROTECTION LEVEL: WINDOWS LIGHT. [SC] QueryServiceConfig2 SUCCESS SERVICE UsoSvc PROTECTION LEVEL: NONE. # WINDOWS LIGHT == SERVICE_LAUNCH_PROTECTED_WINDOWS_LIGHT == 2. # Documented as reserved for internal Windows use. Not a permissions fault. # Note DoSvc is protected too, and wuauserv is not - which is the whole point.

If it says WINDOWS LIGHT or WINDOWS, stop investigating access. The answer is "by design, and there is no supported way around it". Move on to finding out what changed your config, which is a different question.

Step 2 — establish whether your value ever applied. Read both hives in one go, before you assume a revert happened. On this device:

Windows PowerShell — both policy hives, side by side (real output)
# A. Classic Group Policy hive HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate TargetReleaseVersion = 1 TargetReleaseVersionInfo = 24H2 (exactly two values - nothing else) HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU exists = True valueCount = 0 <-- key present, completely empty # B. Policy Manager hive - where the Intune update ring actually lands HKLM\SOFTWARE\Microsoft\PolicyManager\current\device\Update valueCount = 65 AllowAutoUpdate = 6 AllowMUUpdateService = 1 ExcludeWUDriversInQualityUpdate = 1 DeferQualityUpdatesPeriodInDays = 7 DeferFeatureUpdatesPeriodInDays = 0 PauseQualityUpdates = 0 PauseFeatureUpdates = 0 ConfigureDeadlineForQualityUpdates = 30 ConfigureDeadlineForFeatureUpdates = 2 ConfigureDeadlineGracePeriod = 7 ConfigureDeadlineNoAutoReboot = 1 ConfigureFeatureUpdateUninstallPeriod = 30 SetDisablePauseUXAccess = 1 UpdateNotificationLevel = 2 TargetReleaseVersion = <-- present but EMPTY TargetReleaseVersion_LastWrite = 1 <-- and it was written at some point QualityUpdateEnrolled = 0 FeatureUpdateEnrolled = 1 DriverUpdateEnrolled = 1 ...plus _ProviderSet / _WinningProvider companions for most of the above

Two lessons in one screen. The AU subkey exists and holds zero values, so a Test-Path check on it returns True on a device with no legacy Automatic Update policy whatsoever — test for specific values and report the value count instead. And TargetReleaseVersion appears in both hives with different content: set in the GP hive, present-but-empty in the CSP hive with a _LastWrite marker showing something wrote it.

Step 3 — read the provider columns. Those _WinningProvider values are how you find out which management channel owns each setting. On this device they resolve to two distinct GUIDs, and the split is informative: one provider owns the classic ring settings (deferrals, deadlines, notification level), a different one owns the *Enrolled settings. Two providers writing into the same policy area is normal in a modern estate, and it is exactly the situation in which "who set this?" has a non-obvious answer. If a setting is not behaving, check whether its _WinningProvider is the channel you think you are configuring.

Step 4 — read MDMWinsOverGP, because it decides the precedence.

Windows PowerShell — ControlPolicyConflict (real output)
PS> $c = 'HKLM:\SOFTWARE\Microsoft\PolicyManager\current\device\ControlPolicyConflict' PS> (Get-Item $c).Property | ForEach-Object { "$_ = $((Get-ItemProperty $c -Name $_).$_)" } MDMWinsOverGP = 1 MDMWinsOverGP_ProviderSet = 1 MDMWinsOverGP_WinningProvider = CFC160FB-... # Documented default is 0. Set to 1 here, by the same provider that owns # the update ring settings. So Policy CSP settings block their GP equivalents, # and per the docs, script-written values that conflict get removed at each sync.

This is your single best predictor of mechanism two. If MDMWinsOverGP is 1 and the value you keep losing has an MDM equivalent, the documented behaviour is that it gets removed on sync — and you should stop investigating the medic.

Note the nuance on precedence, because the two documents say different-sounding things and both are right. The general Windows Update guidance states plainly that for Windows updates, "Group Policy settings take precedence over MDM". The ControlPolicyConflict documentation says MDMWinsOverGP flips that — but "only applies to policies in Policy CSP" and only "where applicable; not all Group Policies are available via MDM or CSP". An empty CSP value is not a configured CSP policy, which is why the GP-side TargetReleaseVersion on this device still stands.

Step 5 — timestamp the medic's activity. The .etl filenames in C:\Windows\Logs\waasmedic are timestamped, so the folder listing is a run timeline. Compare the times to when your value went away. If the nearest medic start is nowhere near your revert, the medic did not do it. If your revert lines up with an MDM sync instead, you have your answer.

Step 6 — check whether the medic actually did anything. A 16 KB trace means it started and found nothing. A trace in the hundreds of KB means it did work. Two large files in a week, as measured above, is what a healthy device looks like — the medic is running constantly and repairing essentially nothing.

Step 7 — confirm the update client is still being driven. This separates "my policy reverted" from "my client is dead". There is no medic channel, but the Windows Update client has one, and it is the right place to prove the stack is alive.

Microsoft-Windows-WindowsUpdateClient/Operational
Event IDMessageWhat it tells you
26Windows Update successfully found N updatesScans are completing. Observed 344 times in the last 400 events on this device. Careful: "found 0 updates" is emitted identically by a fully-patched device and by a device excluded by a stale pin, so this line alone proves nothing about applicability.
41An update was downloadedContent is actually arriving. 55 occurrences in the same window. If your complaint is "nothing installs" and you have plenty of 41s, the problem is downstream of download.
25Failure entries from the update clientOne occurrence here. Worth reading in full, but do not assume the embedded code is documented — on this same device, 0x80240438 appears repeatedly in Event 25 entries and is not enumerated in Microsoft's published error list at all.

Step 8 — land on one of three root causes. With steps 1 to 7 done, the diagnosis is forced rather than guessed:

What you observedCulpritHow to prove it
A service state or update component you broke by hand came back to platform default; a large .etl sits at roughly the right time.WaaSMedicSvc remediated it.Trace timestamp correlates with the revert; the reverted item is an update component (service state, task, permissions), not a policy value.
A registry value you wrote by hand or by script vanished, on a multi-hour rhythm; MDMWinsOverGP = 1; the value has an MDM equivalent.Policy Manager removed it at MDM sync. Documented behaviour.The value has a Policy CSP equivalent; the revert cadence matches sync, not the weekly task; medic traces at that time are 16 KB stubs.
The value is simply not where you looked, or you never saw it apply at all.Nothing reverted. Wrong hive.It is present under PolicyManager\current\device\Update while you were reading Policies\...\WindowsUpdate, or vice versa.
Both hives disagree and the setting behaves inconsistently between reboots.The documented race condition from configuring one setting in two channels.Same setting present in both hives with different values; _WinningProvider shows a channel you did not expect.

The fix: the supported control plane, per intent

Start from the honest position: there is no supported way to disable the Windows Update Medic Service, and the routes that appear to work are worse than they look.

Writing Start = 4 straight into the service registry key bypasses the SCM, so the protection check never runs and the write succeeds. That is exactly why the recipe circulates. It is also why it is a bad idea, for three separate reasons rather than one. It is not a supported configuration, so nothing Microsoft ships is tested against it. It is precisely the tampering the medic exists to detect and reverse, so it is unstable by design. And you have hand-edited a servicing component, which means a future cumulative or UUS update lands on a machine in a state nobody validated — and when servicing then fails, the failure will not point at what you did months earlier.

Watch out: the same applies, more strongly, to taking ownership of the service key or the task file to re-permission them. As measured above, the ACLs were never what stopped you, so re-ACLing buys nothing even on its own terms — and SetServiceObjectSecurity is on Microsoft's documented list of APIs unprotected callers cannot use against a protected service, so the supported interface will not do it either. Whatever you achieve by going around the SCM, you achieve on a device that no longer matches your baseline, in a component that no update is tested against. Do not ship this to a fleet.

Which is fine, because in every case I have seen, "disable the medic" was never the actual goal. It was a workaround for a goal the platform supports directly. Map the intent to the supported control instead:

What you actually wantSupported controlNotes
Stop updates during a change freezePauseQualityUpdates / PauseFeatureUpdates, or the pause controls in an Intune update ringBounded and reversible by design. Microsoft's guidance is to leave pause disabled "unless there's a known issue requiring time for a resolution" — it is a freeze, not a posture.
Hold a fleet on a specific Windows releaseTargetReleaseVersion plus TargetReleaseVersionInfo, or ProductVersionThe documented pin. See the caveat in the next section — a pin is not a wall.
Delay monthly updates for a validation ringDeferQualityUpdatesPeriodInDays (0-30), DeferFeatureUpdatesPeriodInDays (0-365)Microsoft recommends at most two to three days for quality deferral, and 0 for feature deferral, preferring pause over long deferrals.
Stop surprise rebootsDeadline and grace-period settings, plus active hoursDeadlines 2-30 days, grace 0-7. Microsoft's stated recommendation is to keep deferral + deadline + grace within 7 days of the publish date.
Control the install/restart behaviourAllowAutoUpdate, via the update ring's "Automatic update behavior"See the value-mapping gotcha below.
Stop a specific bad updateRing-level pause or an Intune feature-update policy, not local tamperingNote the documented limit: "Feature update policies don't downgrade devices."
Gotcha: this device holds AllowAutoUpdate = 6, and 6 is not in the documented value list — the Policy CSP reference enumerates 0 through 5. The Intune update-ring settings reference offers a "Reset to default" option for Automatic update behavior and cites AllowAutoUpdate as its CSP, without publishing a number for it, so that is the plausible mapping. Plausible is not documented. Treat 6 as observed, not documented and do not build a compliance check that asserts a meaning for it. The failure mode is nasty: a script that flags "anything not 0-5 is misconfigured" will condemn a correctly-configured fleet.

Then clean up the thing that is probably causing your reverts. Microsoft's own guidance is blunt about accumulated policy: if update velocity is inconsistent across devices, "it might be time to clear all policies and settings and specify only the recommended update policies", and older policies are not removed for you, so "if you set a new policy without disabling a similar older policy, you could have conflicting behavior and updates might not perform as expected."

Concretely, in order:

  1. Pick one channel per setting. Not one channel overall — one channel per setting. The documented race condition bites per-setting.
  2. Decide MDMWinsOverGP deliberately rather than inheriting it. If it is 1, accept that Policy CSP settings block their GP equivalents and that conflicting script-written values will be removed at each sync. Note the documented wrinkle for older builds: on Windows 10 1803 the policy supports neither Delete nor being set back to 0 once set to 1.
  3. Retire legacy Automatic Update policy properly. Several of the older policies are documented as "a legacy policy and isn't applicable for Windows 11", and legacy policies "might be removed in a future release". An empty AU key that still exists is not evidence of anything; go by specific values.
  4. Use the supported diagnostic for blocked GP settings. The MDM Advanced Diagnostic Report includes a list of GP settings blocked because an MDM equivalent is configured. It is at Settings > Accounts > Access work or school > the work account > Advanced Diagnostic Report > Create Report. That list is the direct answer to "why is my GPO not applying", and it beats guessing from the registry.
  5. Leave the medic alone. Once the config is delivered through a supported channel, there is nothing for it to fight. A device under correct policy control has a medic that starts often, finds nothing, and writes 16 KB traces.

That last point is the one to internalise. The medic reverting your changes is not a bug you need to defeat — it is a signal that you configured Windows Update through a channel Windows treats as tampering. Fix the channel and the symptom disappears on its own.

Proof it worked: what a correctly-controlled device looks like

Here is the reference state, measured read-only on a Windows 11 Enterprise 25H2 device, build 26200.9168, managed by Intune update rings.

The medic is present, protected, idle and healthy. WaaSMedicSvc is Stopped with start type Manual, protection level WINDOWS LIGHT, hosted by svchost.exe -k wusvcs -p as the only member of that group. Its scheduled task is Ready with LastTaskResult = 0 and zero missed runs. Stopped is the correct steady state here: it is a demand-start service that runs, checks, and exits.

Tip: "WaaSMedicSvc is Stopped" is not a fault, in the same way that TrustedInstaller sitting Stopped/Manual on an idle device is not a fault. Both are among the most common false alarms in update triage. The healthy idle baseline on this device is wuauserv Running/Manual, bits Running/Automatic, cryptsvc Running/Automatic, UsoSvc and DoSvc Running/Automatic, and trustedinstaller, msiserver and WaaSMedicSvc all Stopped/Manual.

The policy is where it should be, and only there. Sixty-five values under PolicyManager\current\device\Update, mapping one-to-one onto documented Intune update-ring settings — AllowMUUpdateService, ExcludeWUDriversInQualityUpdate, DeferQualityUpdatesPeriodInDays, DeferFeatureUpdatesPeriodInDays, ConfigureFeatureUpdateUninstallPeriod, SetDisablePauseUXAccess, UpdateNotificationLevel, and the four deadline settings. The classic GP hive holds exactly two values. That is what "configured through the supported channel" looks like in the registry, and it is the shape you should be able to point at when someone asks whether a device is under control.

The medic is doing nothing, loudly. 151 traces over seven days, 139 of them at the 16 KB floor, 30 starts in the last 24 hours, two files over 128 KB. Constant activity, almost no repairs. If you inherit a device where most traces are large, that is the anomaly worth chasing — and it tells you something is repeatedly breaking update components, which is a much more interesting question than "how do I stop the medic".

The update client is being driven normally. In the last 400 Operational events: Event 26 three hundred and forty-four times, Event 41 fifty-five times, Event 25 once.

And one finding worth carrying away, because it reframes what a pin is:

Windows PowerShell — the pin that got overtaken (real output)
Policy TargetReleaseVersionInfo : 24H2 # what the GP hive pins Installed DisplayVersion : 25H2 # what is actually running CurrentBuild.UBR : 26200.9168 BuildLabEx : 26100.1.amd64fre.ge_release.240331-1435 KB5054156 installed : 2026-02-04 # the 25H2 enablement package # ProductName reads "Windows 10 Enterprise" on this Windows 11 device. # ReleaseId reads 2009. Neither is usable for version detection.

The device is pinned to 24H2 and running 25H2. It moved up via the enablement package, and the BuildLabEx string still carries the 24H2 lab signature — forensic proof it was installed as 24H2 and promoted later. So before you conclude that the medic or anything else "changed my release", check whether an enablement package simply walked past your pin.

Two detection traps in that same output, both worth writing into your scripts today. ProductName says "Windows 10 Enterprise" on a Windows 11 device, so anything keying on it to detect Windows 11 fails silently — use CurrentBuild (22000 or higher) or DisplayVersion. And ReleaseId is frozen at 2009. Neither is a version source.

What to record. Before you change anything, capture: sc qprotection for the update services, the value list from both policy hives, MDMWinsOverGP, the PerformRemediation task info, and a directory listing of C:\Windows\Logs\waasmedic with sizes. That is five read-only commands and it is the difference between "it reverted again" and a diagnosis you can hand to someone else.

References

Everything measured in this post came from read-only queries against a live Windows 11 Enterprise 25H2 device (build 26200.9168): registry reads, version resources, sc qprotection, sc sdshow, Get-Acl, OpenService handle checks that were immediately closed, Export-ScheduledTask, and directory listings. No service was stopped, reconfigured or re-permissioned, and no policy was changed. Where a detail is not published by Microsoft it is labelled observed, not documented rather than asserted.

Was this post helpful?
React below — no account needed
Share this post
LinkedIn X / Twitter Reddit Bluesky

More from EndpointWeekly

Windows Update
A WSUS device installed something WSUS never approved:…
A device pointed at WSUS pulls updates straight from Microsoft, and nothing errors. Here…
Windows Update
Your Intune update ring says Succeeded: where the policy…
Intune reports delivery of policy, not effective configuration. MDM update settings land…
Windows Update
One read-only PowerShell collector for Windows patching failures…
Test-Path and value checks return confidently wrong patching verdicts on real devices.…