<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[AdminPackStudio]]></title><description><![CDATA[AdminPackStudio]]></description><link>https://adminpackstudio.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sat, 31 Oct 2026 09:56:55 GMT</lastBuildDate><atom:link href="https://adminpackstudio.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[WinRM Cannot Complete the Operation: A Read-First Name, Port, Listener, Firewall Profile, and TrustedHosts Triage]]></title><description><![CDATA[WinRM cannot complete the operation? Read the full error, DNS and port 5985, Test-WSMan, the listener, and the Public-profile firewall rule first.]]></description><link>https://adminpackstudio.hashnode.dev/winrm-cannot-complete-the-operation-a-read-first-name-port-listener-firewall-profile-and-trustedhosts-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/winrm-cannot-complete-the-operation-a-read-first-name-port-listener-firewall-profile-and-trustedhosts-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 18:48:30 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "From JUMP01, <code>Enter-PSSession -ComputerName FS02</code> fails with 'Connecting to remote server FS02 failed with the following error message : WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer.' RDP to FS02 works, and <code>Invoke-Command</code> to FS01 from the same box works. A forum post says to run <code>Set-Item WSMan:\localhost\Client\TrustedHosts -Value *</code> here and <code>Enable-PSRemoting -SkipNetworkProfileCheck -Force</code> on every server. Can we do that?"</p>
<p>Not yet. <strong>The <em>WinRM cannot complete the operation</em> error isn't an authentication failure. It's the client telling you it never got a WS-Management answer from that name on that port: the name resolved somewhere else, the path or a network firewall dropped TCP 5985, the WinRM service or its listener isn't there, the Windows Firewall rule on the target is off or scoped to the local subnet, the target's network profile flipped to Public, or a proxy got in the way.</strong> Microsoft's own copy of the message, from Server Manager's troubleshooting guide, says as much: "Windows Remote Management (WinRM) cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the Windows Remote Management service is enabled and allows access from this computer. By default, the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet." TrustedHosts doesn't touch any of that. Microsoft describes it as an authentication setting: "you should consider the TrustedHosts setting to be the list of hosts for which you wish to suppress the error generated by being unable to verify the server's identity." Work through it as a chain: <strong>which command, which name, from where, and what else works?</strong> → <strong>what does the whole error say?</strong> → <strong>does the name resolve to the right address, and does TCP 5985 answer?</strong> → <strong>does WinRM itself answer (<code>Test-WSMan</code>)?</strong> → <strong>on the target: is the service running and is there a listener on the right addresses?</strong> → <strong>is the firewall rule on, in which profile, scoped to which addresses, and what profile is the network in?</strong> → <strong>is a proxy in the path?</strong> → <strong>once it connects, does the error change to an authentication one?</strong> → <strong>change one link</strong> → <strong>prove it from the same client.</strong> <code>Resolve-DnsName</code>, <code>Test-NetConnection</code>, <code>Test-WSMan</code>, <code>Get-Service</code>, <code>winrm enumerate winrm/config/listener</code>, <code>Get-WSManInstance</code>, <code>Get-NetConnectionProfile</code>, <code>Get-NetFirewallRule</code>, <code>Get-NetFirewallAddressFilter</code>, <code>netsh winhttp show advproxy</code>, and <code>setspn -Q</code> are the sources for that chain. Set TrustedHosts to <code>*</code> just in case and you've told every PowerShell session on that box to skip server identity checks for every name, while the firewall rule that dropped the packet is still dropping it.</p>
<p>If the same server also fails Kerberos to file shares or SQL with a target name error, <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> covers the DNS, password, and SPN side. If the remoting failure shows up during Hyper-V live migration or remote host management, <a href="https://dev.to/adminpackstudio/hyper-v-live-migration-failed-event-21502-a-read-first-switch-processor-version-and-kerberos-181f">Hyper-V Live Migration Failed (Event 21502)</a> covers the migration side. This post covers **"a Windows client or server that fails <code>Enter-PSSession</code>, <code>Invoke-Command</code>, <code>New-PSSession</code>, <code>Test-WSMan</code>, Windows Admin Center, Server Manager, or Hyper-V Manager to a Windows Server target with 'WinRM cannot complete the operation', with or without the related 'connection to the remote host was refused', 'client cannot connect to the destination', 'cannot process the request', or OperationTimeout errors, and you need a read-first path before you set TrustedHosts to <em>, open the Public firewall rule to Any, or re-run Enable-PSRemoting everywhere."</em>*</p>
<h2>TL;DR</h2>
<ul>
<li><strong>Read the whole error, not the first five words.</strong> <em>WinRM cannot complete the operation</em> means no answer came back; "The WinRM client cannot process the request" and "WinRM cannot process the request" mean an answer came back and authentication failed. They have different fixes (Step 2).</li>
<li><strong>Prove the path before the service.</strong> <code>Test-NetConnection -ComputerName FS02 -Port 5985</code> tells you whether the name resolves and TCP 5985 answers from this client. A failure there is DNS, routing, or a firewall, not PowerShell (Step 3).</li>
<li><strong><code>Test-WSMan</code> is the cheapest WinRM probe.</strong> Microsoft: it "submits an identification request that determines whether the WinRM service is running on a local or remote computer." Without <code>-Authentication</code> it's sent anonymously, so it separates a missing listener from missing rights (Step 4).</li>
<li><strong>A running service isn't a listener.</strong> Microsoft: "By default, no WinRM listener is configured. Even if the WinRM service is running, WS-Management protocol messages that request data can't be received or sent." A policy-made listener with an empty filter listens on nothing (Step 5).</li>
<li><strong>The Public profile is the classic cause.</strong> Microsoft: Enable-PSRemoting "also creates a firewall rule for public networks that allows remote access only from computers in the same local subnet." A server whose NIC fell to Public answers its own subnet and nobody else (Step 6).</li>
<li><strong>TrustedHosts is for authentication, not reachability.</strong> Microsoft: "The computers in the trusted hosts list aren't authenticated." Use it only for the IP-address or workgroup case it was made for, with a specific entry (Steps 8 and 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>The exact command, the name or IP typed, the client and target, their domain or workgroup, the tool, the network path, and which other targets work</td>
<td>"Remoting is broken on FS02" with no client, no command, and no note that RDP works</td>
</tr>
<tr>
<td>2. Error read</td>
<td>The full message is copied and classified as no answer, refused, timeout, name resolution, or authentication</td>
<td>Every remoting error treated as the same problem and fixed with TrustedHosts</td>
</tr>
<tr>
<td>3. Name and path</td>
<td>The name resolves to the target's current address, and TCP 5985 (or 5986) answers from this client</td>
<td>A stale A record, a second NIC registered in DNS, or a network firewall between sites</td>
</tr>
<tr>
<td>4. WinRM answers</td>
<td><code>Test-WSMan</code> returns the identity response from this client</td>
<td>Port open but no WS-Management answer, or a different service on the port</td>
</tr>
<tr>
<td>5. Service and listener</td>
<td>WinRM is Running, a listener exists for the transport you use, and ListeningOn lists the target's addresses</td>
<td>Service disabled on a client OS; a policy listener with an empty IPv4 filter; a changed port</td>
</tr>
<tr>
<td>6. Firewall and profile</td>
<td>The WinRM inbound rule is enabled for the profile the NIC is actually in, with a remote address scope that includes this client</td>
<td>NIC in Public, rule scoped to Local subnet, or a policy that disabled the rule</td>
</tr>
<tr>
<td>7. Proxy</td>
<td>No WinHTTP or session proxy sends WinRM traffic somewhere it shouldn't go</td>
<td>A machine-wide WinHTTP proxy left from a deployment tool</td>
</tr>
<tr>
<td>8. Authentication</td>
<td>Once it connects, the account and method work: Kerberos by name, or NTLM by IP with TrustedHosts or HTTPS and explicit credentials</td>
<td>Error changes to "cannot process the request" and someone sets TrustedHosts to <code>*</code></td>
</tr>
<tr>
<td>9. Change</td>
<td>One link fixed on the right side (client or target), with the reason written down</td>
<td>Enable-PSRemoting, TrustedHosts <code>*</code>, firewall Any, and a proxy reset all in one go</td>
</tr>
<tr>
<td>10. Proof</td>
<td>The same command from the same client works, and still works after a reboot and a policy refresh</td>
<td>Works today, fails after the next reboot when the profile drops to Public again</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>The exact command and what was typed for the computer name</strong></td>
<td>Short name, FQDN, and IP address take different authentication paths, and the error text names which one failed (Steps 2 and 8)</td>
</tr>
<tr>
<td><strong>The client it ran from, and the target</strong></td>
<td>Most fixes are on one side only: TrustedHosts and proxy are on the client; the service, listener, firewall rule, and profile are on the target</td>
</tr>
<tr>
<td><strong>Domain, trusted domain, workgroup, or Microsoft Entra-only on each side</strong></td>
<td>Kerberos works only by name inside a domain or across a trust; everything else is NTLM and needs HTTPS or TrustedHosts (Step 8)</td>
</tr>
<tr>
<td><strong>The tool: PowerShell, Windows Admin Center, Server Manager, Hyper-V Manager, or a monitoring product</strong></td>
<td>They all use WinRM, but each wraps the error in its own text; Server Manager and WAC have their own documented checks (Step 2)</td>
</tr>
<tr>
<td><strong>The network path: same subnet, another site, VPN, or another VLAN</strong></td>
<td>The Public-profile rule allows only the local subnet, so "works from the server next to it, fails from the jump box" is a clue (Step 6)</td>
</tr>
<tr>
<td><strong>What else works from the same client to the same target</strong></td>
<td>RDP, ping, SMB, or <code>Invoke-Command</code> to a neighbor narrow it to the WinRM port, rule, or service on this target</td>
</tr>
<tr>
<td><strong>What changed</strong></td>
<td>A new server build, an in-place upgrade, a NIC or VLAN change, a new GPO, a security baseline, or a deployment tool that set a proxy</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td><em>WinRM cannot complete the operation</em> to one target only</td>
<td>The path, the listener, the firewall rule, or the profile on that target</td>
<td>Steps 3 to 6</td>
</tr>
<tr>
<td>The same error from one client to every target</td>
<td>Something on the client: a proxy, DNS, or an outbound firewall</td>
<td>Steps 3 and 7</td>
</tr>
<tr>
<td>It works from the same subnet but not from another</td>
<td>The Public-profile rule scoped to the local subnet, or a network firewall</td>
<td>Step 6</td>
</tr>
<tr>
<td>It works by FQDN but not by IP address</td>
<td>Not a reachability problem at all; IP means NTLM, which needs TrustedHosts or HTTPS</td>
<td>Step 8</td>
</tr>
<tr>
<td>"The connection to the remote host was refused"</td>
<td>Something answered and refused; usually no listener on that port</td>
<td>Step 5</td>
</tr>
<tr>
<td>"The WinRM client cannot process the request" or "WinRM cannot process the request"</td>
<td>The connection worked and authentication failed</td>
<td>Step 8</td>
</tr>
<tr>
<td>"Access is denied"</td>
<td>The account isn't allowed on the endpoint, or the shell isn't elevated for local WSMan changes</td>
<td>Step 5</td>
</tr>
<tr>
<td>It started after an in-place upgrade or a client OS rebuild</td>
<td>Client versions of Windows have the WinRM service disabled by default; a reset profile or trusted hosts list</td>
<td>Steps 5 and 6</td>
</tr>
<tr>
<td>Server Manager shows the target as not accessible</td>
<td>Microsoft's table puts this error under "Target computer not accessible" network connection errors</td>
<td>Steps 3 and 6</td>
</tr>
<tr>
<td>Hyper-V Manager can't connect to a remote host</td>
<td>Microsoft's Hyper-V page: "To manage remote Hyper-V hosts, enable remote management on both the local computer and the remote host."</td>
<td>Steps 5 and 6 on the host</td>
</tr>
<tr>
<td>Windows Admin Center can reach some servers but not others</td>
<td>Microsoft's WAC page: test <code>Enter-PSSession</code> from the gateway itself</td>
<td>Steps 3 to 6 from the gateway</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the whole error (read-only)</h2>
<p>Copy the full text, including the part after the first sentence. In PowerShell, the last error holds it:</p>
<pre><code class="language-powershell"># Read-only: the full text of the last remoting error, and its error ID
$Error[0].Exception.Message
$Error[0].FullyQualifiedErrorId
</code></pre>
<p>Then match it against Microsoft's messages. They look alike and they point at different links:</p>
<table>
<thead>
<tr>
<th>Microsoft's message (excerpt)</th>
<th>Where Microsoft lists it</th>
<th>What it tells you (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>"Windows Remote Management (WinRM) cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the Windows Remote Management service is enabled and allows access from this computer."</td>
<td>Server Manager troubleshooting guide, "Target computer not accessible"</td>
<td>No WS-Management answer came back; name, path, firewall rule, profile, or listener (Steps 3 to 6)</td>
</tr>
<tr>
<td>"The connection to the remote host was refused. Verify that the WS-Management service is running on the remote host and configured to listen for requests on the correct port and HTTP URL."</td>
<td>about_Remote_Troubleshooting, enable remoting sections</td>
<td>The host answered and refused; usually no listener on that port, or remoting disabled (Step 5)</td>
</tr>
<tr>
<td>"The client cannot connect to the destination specified in the request. Verify that the service on the destination is running and is accepting requests."</td>
<td>about_Remote_Troubleshooting, proxy and firewall policy sections</td>
<td>A proxy in the way, or a policy-made listener with no addresses (Steps 5 and 7)</td>
</tr>
<tr>
<td>"The WinRM client cannot process the request because the server name cannot be resolved."</td>
<td>Server Manager troubleshooting guide, name resolution error</td>
<td>DNS on the client can't find that name (Step 3)</td>
</tr>
<tr>
<td>"The WS-Management service cannot complete the operation within the time specified in OperationTimeout."</td>
<td>about_Remote_Troubleshooting, timeout errors</td>
<td>The session connected and a command ran too long; not a connection problem</td>
</tr>
<tr>
<td>"The WinRM client cannot process the request. If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting."</td>
<td>about_Remote_Troubleshooting, IP address and workgroup sections</td>
<td>Connected; NTLM was needed and neither HTTPS nor TrustedHosts was set (Step 8)</td>
</tr>
<tr>
<td>"Default authentication may be used with an IP address under the following conditions: the transport is HTTPS or the destination is in the TrustedHosts list, and explicit credentials are provided."</td>
<td>Server Manager troubleshooting guide, Default authentication error</td>
<td>Connected by IP; same fix as the row above, plus <code>-Credential</code> (Step 8)</td>
</tr>
<tr>
<td>"WinRM cannot process the request. The following error with errorcode 0x80090322 occurred while using Kerberos authentication: An unknown security error occurred."</td>
<td>Server Manager troubleshooting guide, Kerberos security error</td>
<td>Connected; Kerberos failed, often an SPN or a name that points at another account (Step 8)</td>
</tr>
<tr>
<td>"The I/O operation has been aborted because of either a thread exit or an application request."</td>
<td>about_Remote_Troubleshooting, operation failure</td>
<td>WinRM stopped or restarted while the command ran; Microsoft's fix is to start the service and retry</td>
</tr>
</tbody></table>
<p>Two details matter when you compare with forum posts. First, the text PowerShell prints usually says <em>WinRM</em> where Microsoft's Server Manager copy says <em>Windows Remote Management (WinRM)</em>, and <em>the WinRM service</em> where it says <em>the Windows Remote Management service</em>; read it as the same no-answer case (our reading). Second, Microsoft's Server Manager table lists the same no-answer text for workgroup targets where TrustedHosts was already set, and gives the cause as "a subnet timeout error occurs if the user has not changed the default subnet restrictions for computers in workgroups". That's the firewall scope (Step 6), not authentication: adding the server to TrustedHosts a second time won't change it.</p>
<hr />
<h2>Step 3: Does the name resolve, and does the port answer? (read-only)</h2>
<p>Run these from the client that fails, by the same name the failing command used:</p>
<pre><code class="language-powershell"># Read-only, on the client: what the name resolves to from here
Resolve-DnsName -Name FS02.contoso.com -Type A_AAAA

# Read-only, on the client: name resolution, route, and TCP 5985 (WinRM over HTTP) in one test
Test-NetConnection -ComputerName FS02.contoso.com -Port 5985 -InformationLevel Detailed

# Read-only, on the client: TCP 5986 (WinRM over HTTPS), if the command uses -UseSSL
Test-NetConnection -ComputerName FS02.contoso.com -Port 5986
</code></pre>
<p>Then, on the target's console or an RDP session (remoting to it is the thing that's broken), read the addresses it actually has:</p>
<pre><code class="language-powershell"># Read-only, on the target: its IPv4 addresses, to compare with what DNS returned on the client
Get-NetIPAddress -AddressFamily IPv4 | Select-Object -Property InterfaceAlias, IPAddress, PrefixLength
</code></pre>
<p>The ports are Microsoft's defaults: "PowerShell Remoting with WinRM listens on the following ports" HTTP 5985 and HTTPS 5986, and the Invoke-Command reference adds "The default ports are 5985 (WinRM port for HTTP) and 5986 (WinRM port for HTTPS)." Older guidance with port 80 and 443 is from before WinRM 2.0; Microsoft's HTTPS article: "On earlier versions of Windows, WinRM HTTP uses port 80 and WinRM HTTPS uses port 443."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>DNS returns an address the target doesn't have</td>
<td>A stale record, a second adapter registered in DNS, or the wrong zone; the client is knocking on another box. Fix DNS before anything else</td>
</tr>
<tr>
<td>The name doesn't resolve at all</td>
<td>Microsoft's "The WinRM client cannot process the request because the server name cannot be resolved." case, or a short name with no matching DNS suffix on this client; try the FQDN</td>
</tr>
<tr>
<td><code>TcpTestSucceeded : True</code> on 5985</td>
<td>The path and something listening on the port are fine from this client; go to Step 4</td>
</tr>
<tr>
<td><code>TcpTestSucceeded</code> False, and the same test from a server in the target's own subnet is True</td>
<td>The target's rule allows only the local subnet (the Public profile case), or a network firewall between the subnets blocks 5985 (Step 6)</td>
</tr>
<tr>
<td><code>TcpTestSucceeded</code> False from everywhere, RDP to the target works</td>
<td>Nothing is listening on 5985, or the host firewall drops it for every source (Steps 5 and 6)</td>
</tr>
<tr>
<td>Ping fails but TCP succeeds</td>
<td>ICMP is blocked; ignore ping and trust the TCP result</td>
</tr>
<tr>
<td>5985 fails, 5986 succeeds</td>
<td>The target only has an HTTPS listener; use <code>-UseSSL</code> or add an HTTP listener deliberately (Step 9)</td>
</tr>
</tbody></table>
<p>If the path fails only from one site or VPN, check the network firewall between them for TCP 5985 before you touch the server. Microsoft's Server Manager guide gives the same first suspect for this message: "The network connecting the client and server is offline, or there is a problem with the target server's connection to the network."</p>
<hr />
<h2>Step 4: Does WinRM itself answer? (read-only)</h2>
<p>An open port isn't proof that WinRM is behind it. <code>Test-WSMan</code> asks WinRM directly. Microsoft: it "submits an identification request that determines whether the WinRM service is running on a local or remote computer."</p>
<pre><code class="language-powershell"># Read-only, on the client: anonymous identify request to the default port
Test-WSMan -ComputerName FS02.contoso.com

# Read-only, on the client: the same request with authentication (also returns the OS version)
Test-WSMan -ComputerName FS02.contoso.com -Authentication Default

# Read-only, on the client: over HTTPS, if the target has an HTTPS listener
Test-WSMan -ComputerName FS02.contoso.com -UseSSL
</code></pre>
<p>Microsoft explains why the first one is useful: "If you do not specify the Authentication parameter, the Test-WSMan request is sent to the remote computer anonymously, without using authentication." So the two forms split the problem:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Anonymous <code>Test-WSMan</code> fails with the same "cannot complete the operation" text</td>
<td>WinRM isn't reachable from here; Steps 5 and 6 on the target</td>
</tr>
<tr>
<td>Anonymous works, <code>-Authentication Default</code> fails</td>
<td>Reachability is fine; it's an authentication problem (Step 8)</td>
</tr>
<tr>
<td>Both work, <code>Enter-PSSession</code> still fails</td>
<td>The session endpoint, permissions, or a session proxy option; Steps 5, 7, and 8</td>
</tr>
<tr>
<td><code>Test-WSMan</code> by FQDN works, by IP address fails</td>
<td>IP means NTLM; see Step 8</td>
</tr>
<tr>
<td><code>-UseSSL</code> fails with a certificate error</td>
<td>The HTTPS listener exists; its certificate doesn't match the name or isn't trusted (Step 9)</td>
</tr>
</tbody></table>
<p>For Windows Admin Center, run the same tests from the gateway, not from your browser's machine: it's the gateway that opens the WinRM session. Microsoft's WAC page says a failed <code>Enter-PSSession</code> from the gateway "might relate to WinRM issues, network connectivity problems, or authorization issues."</p>
<hr />
<h2>Step 5: On the target, is the service running and is there a listener? (read-only)</h2>
<p>Read these on the target, from its console or RDP, in an elevated PowerShell window. Microsoft: "You must have administrative rights to view or change settings for the local computer in the WSMan: drive."</p>
<pre><code class="language-powershell"># Read-only, on the target: the WinRM service and its startup type
Get-Service -Name WinRM | Select-Object -Property Name, Status, StartType

# Read-only, on the target: the listeners, in Microsoft's form
winrm enumerate winrm/config/listener

# Read-only, on the target: the same listeners through PowerShell, with Source and ListeningOn
Get-WSManInstance winrm/config/listener -Enumerate

# Read-only, on the target: the service settings (auth methods, IPv4Filter, IPv6Filter, ports)
winrm get winrm/config

# Read-only, on the target: does WinRM answer locally, before the network is involved?
Test-WSMan -ComputerName localhost

# Read-only, on the target: the session endpoints and who may use them
Get-PSSessionConfiguration | Format-Table -Property Name, Permission -AutoSize
</code></pre>
<p>Microsoft's defaults to compare with: "On server versions of Windows, the WinRM service startup type is Automatic. However, on client versions of Windows, the WinRM service is disabled by default." And remoting is on by default on servers: "Windows Server 2012 and newer releases of Windows Server are enabled for PowerShell remoting by default." So a Windows Server target without a listener had it removed or never got the default, usually by policy, an image, or a hardening script (our reading).</p>
<p>Read the listener output against Microsoft's listener properties:</p>
<table>
<thead>
<tr>
<th>Property</th>
<th>Microsoft's default or description</th>
<th>What to look for (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Transport</td>
<td>"The value must be either HTTP or HTTPS. The default is HTTP."</td>
<td>The transport your command uses exists; <code>-UseSSL</code> needs an HTTPS listener</td>
</tr>
<tr>
<td>Port</td>
<td>"WinRM 2.0: The default HTTP port is 5985."</td>
<td>Anything else needs <code>-Port</code> on the client; Server Manager can't use a changed port</td>
</tr>
<tr>
<td>Address</td>
<td>"Specifies the address for which this listener is being created."</td>
<td><code>*</code> for all addresses, or the right one</td>
</tr>
<tr>
<td>Enabled</td>
<td>"The default value is True."</td>
<td>False means it exists and does nothing</td>
</tr>
<tr>
<td>Source</td>
<td>Microsoft's policy example shows <code>Source : GPO</code></td>
<td>A policy owns it; local edits won't stick</td>
</tr>
<tr>
<td>ListeningOn</td>
<td>"Specifies the IPv4 and IPv6 addresses that the listener uses."</td>
<td>Empty means it listens on nothing</td>
</tr>
</tbody></table>
<p>The empty ListeningOn case is Microsoft's own example. When the listener policy is misconfigured: "A configuration error in the policy results in an empty value for the ListeningOn property." The fix is in the policy: Microsoft's remoting help says "Enable the policy and specify the IPv4 and IPv6 filters." Its remoting help names the policy Allow automatic configuration of listeners, under Computer Configuration\Administrative Templates\Windows Components\Windows Remote Management (WinRM)\WinRM service; Microsoft's Server Manager page names the setting in that folder "Allow remote server management through WinRM". Read both names as the same listener policy when you look for it in your GPOs (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>WinRM Stopped or Disabled</td>
<td>Nothing to answer; on a client OS that's the default, on a server something disabled it (Step 9)</td>
</tr>
<tr>
<td>No listener at all</td>
<td>Remoting was never enabled or was removed; <code>Test-WSMan -ComputerName localhost</code> fails too (Step 9)</td>
</tr>
<tr>
<td>Listener <code>Source : GPO</code>, <code>ListeningOn : {}</code></td>
<td>Microsoft's policy misconfiguration case: the IPv4 or IPv6 filter in the listener policy is blank or excludes the target's addresses</td>
</tr>
<tr>
<td>Listener on a port other than 5985 or 5986</td>
<td>Microsoft: "If the default authentication mechanism or the WinRM listener port number on a remote server has been changed from default settings, Server Manager cannot communicate with the remote server."</td>
</tr>
<tr>
<td><code>Test-WSMan -ComputerName localhost</code> works, remote fails</td>
<td>The service and listener are fine; it's the firewall rule, profile, or path (Step 6)</td>
</tr>
<tr>
<td><code>winrm e winrm/config/listener</code> fails locally with error 0x80338012</td>
<td>Microsoft's local-functionality case: "This problem may occur if the Window Remote Management service and its listener functionality are broken." (Step 9)</td>
</tr>
<tr>
<td>Endpoints without your group in Permission</td>
<td>Remote sessions will fail with Access is denied after the connection works; Microsoft: connect with "a user account that's a member of the Administrators or Remote Management Users group on the remote computer"</td>
</tr>
<tr>
<td>Two listeners on the same port with different addresses</td>
<td>Microsoft: "If two listener services with different IP addresses are configured with the same port number and computer name, then WinRM listens or receives messages on only one address."</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Is the firewall rule on, for the right profile and scope? (read-only)</h2>
<p>This is the step that matches the second half of the message, "By default, the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet." Read the profile first, then the rules, on the target:</p>
<pre><code class="language-powershell"># Read-only, on the target: the network category of each connected adapter
Get-NetConnectionProfile | Select-Object -Property InterfaceAlias, Name, NetworkCategory

# Read-only, on the target: the WinRM inbound rules, whether they're on, for which profile, and the action
Get-NetFirewallRule -Name 'WINRM*' | Select-Object -Property Name, DisplayName, Enabled, Profile, Action

# Read-only, on the target: the remote address scope of each WinRM rule
Get-NetFirewallRule -Name 'WINRM*' | ForEach-Object -Process {
    $r = $_
    $r | Get-NetFirewallAddressFilter | Select-Object @{n='Rule';e={$r.Name}}, RemoteAddress
}

# Read-only, on the target: the local port on each WinRM rule
Get-NetFirewallRule -Name 'WINRM*' | Get-NetFirewallPortFilter | Select-Object -Property Protocol, LocalPort

# Read-only, on the target: the rules group policy delivers (the RSOP store), which local edits can't override
Get-NetFirewallRule -PolicyStore RSOP | Where-Object -FilterScript { $_.DisplayName -like '*Remote Management*' } |
    Select-Object -Property Name, DisplayName, Enabled, Profile, Action
</code></pre>
<p>Microsoft's rule names come from the Enable-PSRemoting and remoting help pages: <code>WINRM-HTTP-In-TCP</code> and <code>WINRM-HTTP-In-TCP-NoScope</code> in its client example, and <code>WINRM-HTTP-In-TCP-PUBLIC</code> for the Public rule on servers. The display name you'll see in the console is "Windows Remote Management (HTTP-In)". Microsoft also warns: "The name of the firewall rule can be different for different versions of Windows." That's why the read above uses a wildcard.</p>
<p>What Microsoft says the defaults are:</p>
<table>
<thead>
<tr>
<th>Where</th>
<th>Microsoft's text</th>
</tr>
</thead>
<tbody><tr>
<td>Servers, private and domain networks</td>
<td>"the Enable-PSRemoting cmdlet creates firewall rules that allow unrestricted remote access."</td>
</tr>
<tr>
<td>Servers, public networks</td>
<td>"It also creates a firewall rule for public networks that allows remote access only from computers in the same local subnet."</td>
</tr>
<tr>
<td>Clients, public networks</td>
<td>"By default, it fails on public networks, but if you use the SkipNetworkProfileCheck parameter, Enable-PSRemoting succeeds and creates a firewall rule that allows traffic from the same local subnet."</td>
</tr>
<tr>
<td>Any Windows</td>
<td>"On public networks, the default Windows Firewall rule allows PowerShell Remoting connections only from within the same subnet."</td>
</tr>
<tr>
<td><code>winrm quickconfig</code></td>
<td>"The command winrm quickconfig creates a firewall exception only for the current user profile."</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>A domain-joined server's adapter shows NetworkCategory Public</td>
<td>Domain detection failed on that adapter (no DC reachable when the network was identified, wrong DNS, or a second adapter); the Public rule's local-subnet scope now applies to everyone else</td>
</tr>
<tr>
<td>NetworkCategory DomainAuthenticated, the Domain rule enabled and Allow</td>
<td>The host firewall isn't the block for domain traffic; look at the path (Step 3) or a third-party firewall</td>
</tr>
<tr>
<td>The WinRM rule for the active profile is disabled or missing</td>
<td>Something removed or disabled it; check RSOP first, then the local rule (Step 9)</td>
</tr>
<tr>
<td>RemoteAddress <code>LocalSubnet</code> on the rule for the active profile</td>
<td>Exactly the "same local subnet" restriction in the message; clients in other subnets get no answer</td>
</tr>
<tr>
<td>The rule exists in RSOP disabled, or the policy blocks local rules</td>
<td>A GPO owns it; a local <code>Enable-PSRemoting</code> or <code>Set-NetFirewallRule</code> won't stick</td>
</tr>
<tr>
<td>LocalPort isn't 5985</td>
<td>Someone moved the listener or the rule; they must match each other and the client's <code>-Port</code></td>
</tr>
</tbody></table>
<p>On the policy side, Microsoft names two settings. The Windows Firewall: Allow local port exceptions policy decides whether local rules apply at all: "This policy allows members of the Administrators group to create a firewall exception for the Windows Remote Management (WinRM) service." And for Server Manager's version of this message, Microsoft's advice is the same read: "Try verifying that Windows Firewall exceptions have been enabled on the target server for Windows Remote Management (WinRM) inbound rules."</p>
<hr />
<h2>Step 7: Is a proxy in the path? (read-only)</h2>
<p>Microsoft: "Because PowerShell remoting uses the HTTP protocol, it is affected by HTTP proxy settings." Read the machine-wide WinHTTP setting and any session option on the client:</p>
<pre><code class="language-powershell"># Read-only, on the client: the WinHTTP advanced proxy setting (Microsoft's current form)
netsh winhttp show advproxy

# Read-only, on the client: the older form, deprecated but still present on many builds
netsh winhttp show proxy

# Read-only, on the client: a session option set in this session or in a profile script
$PSSessionOption
</code></pre>
<p>Microsoft's proxy notes for WinRM: "By default, WinHTTP is not configured to use a proxy server." and, for WinRM 1.1 and earlier, "the WinRM client relies on the Windows HTTP services (WinHTTP) proxy configuration." On the session side, New-PSSessionOption's ProxyAccessType accepts IEConfig, WinHttpConfig, AutoDetect, NoProxyServer, and None, and "The default value is None."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>WinHTTP shows direct access, <code>$PSSessionOption</code> empty</td>
<td>No proxy on this client; move on</td>
</tr>
<tr>
<td>A WinHTTP proxy with no bypass for internal names</td>
<td>Left by a deployment, update, or security tool; WinRM traffic to internal servers may go to the proxy</td>
</tr>
<tr>
<td><code>$PSSessionOption</code> set with ProxyAccessType in a profile script</td>
<td>Every remoting command in that session inherits it</td>
</tr>
<tr>
<td>The error is "The client cannot connect to the destination specified in the request." rather than "cannot complete the operation"</td>
<td>Microsoft lists that text under its proxy section; this is the more likely step for it</td>
</tr>
</tbody></table>
<p>Microsoft's proxy page is also clear about HTTP through a proxy: "If the proxy server requires authentication, the WinRM clients and servers must use HTTPS." A design that sends WinRM to internal servers through an authenticating proxy over HTTP is the problem, not a setting (our reading).</p>
<hr />
<h2>Step 8: When it connects, does the error change to authentication? (read-only)</h2>
<p>Often, fixing the profile or the rule turns "cannot complete the operation" into one of the "cannot process the request" messages. That's progress: the connection works and the authentication path is now the question. Microsoft's rules for which path you get:</p>
<table>
<thead>
<tr>
<th>How you connect</th>
<th>What Microsoft says happens</th>
<th>What it needs</th>
</tr>
</thead>
<tbody><tr>
<td>By computer name, inside a domain or across a trust</td>
<td>"When a client connects to a domain server using its computer name, the default authentication protocol is Kerberos."</td>
<td>A working SPN for the name you typed; no TrustedHosts needed</td>
</tr>
<tr>
<td>By IP address</td>
<td>"Kerberos authentication doesn't support IP addresses. When you specify an IP address, NTLM authentication is used."</td>
<td>"Configure the computer for HTTPS transport or add the IP addresses of the remote computers to the TrustedHosts list on the local computer." and <code>-Credential</code></td>
</tr>
<tr>
<td>From or to a workgroup computer</td>
<td>"When the local computer isn't in a domain, you must meet the following requirements"</td>
<td>HTTPS or TrustedHosts, "Verify that a password is set on the workgroup-based computer.", and <code>-Credential</code></td>
</tr>
<tr>
<td>Between Microsoft Entra-only joined machines</td>
<td>"WinRM considers the Microsoft Entra-only joined machines as workgroup machines."</td>
<td>Microsoft's TrustedHosts and SPN prefix fix (Step 9)</td>
</tr>
</tbody></table>
<p>On the IP and workgroup rows, Microsoft adds: "Use the Credential parameter in all remote commands. This is required even when you connect as the current user." The Invoke-Command reference says the same: "To use an IP address in the value of ComputerName, the command must include the Credential parameter."</p>
<p>Read what the client already trusts, and the SPNs behind the name you typed:</p>
<pre><code class="language-powershell"># Read-only, on the client: the current TrustedHosts list
Get-Item WSMan:\localhost\Client\TrustedHosts

# Read-only, any domain-joined machine: which account holds the HTTP SPN for the target's names
setspn -Q HTTP/FS02.contoso.com
setspn -Q HTTP/FS02

# Read-only: the SPNs registered on the target's computer account
setspn -L FS02

# Read-only, on the client: request a ticket for the target's HTTP SPN, to see the Kerberos error directly
klist get HTTP/FS02.contoso.com
</code></pre>
<p>WinRM's Kerberos SPN is HTTP/ plus the name. Microsoft's New-PSSessionOption reference explains the classic conflict: the IncludePortInSPN option "Includes the port number in the Service Principal Name (SPN) used for Kerberos authentication", and it exists because "For example, an IIS application that allows for Kerberos authentication can require the default SPN to be registered to a user account that differs from the computer account." If <code>setspn -Q HTTP/FS02.contoso.com</code> returns a service account instead of the FS02 computer account, that's your Kerberos failure (our reading of Microsoft's text).</p>
<p>Read the authentication errors against Microsoft's Server Manager table:</p>
<table>
<thead>
<tr>
<th>Microsoft's message (excerpt)</th>
<th>Microsoft's cause (excerpt)</th>
<th>Next (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>"error with errorcode 0x80090322 occurred while using Kerberos authentication: An unknown security error occurred."</td>
<td>One listed cause: "The Service Principal Name (SPN) for the remote computer name and port does not exist."</td>
<td>The <code>setspn -Q</code> reads above; the SPN side of <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a></td>
</tr>
<tr>
<td>"error with errorcode 0x80090311 occurred while using Kerberos authentication: There are currently no logon servers available to service the logon request."</td>
<td>"Client is in a domain, but the domain controller for the client computer is not accessible."</td>
<td>The client's own DC path, not the target</td>
</tr>
<tr>
<td>"The following error occurred while using Kerberos authentication: Cannot find the computer"</td>
<td>Workgroup target by name, an untrusted domain, or a misspelled domain name</td>
<td>Explicit credentials and TrustedHosts for the workgroup case; fix the name for the typo case</td>
</tr>
<tr>
<td>"Default authentication may be used with an IP address under the following conditions"</td>
<td>Connecting by IP without TrustedHosts or HTTPS</td>
<td>Connect by name, or meet the IP conditions deliberately</td>
</tr>
<tr>
<td>"If the authentication scheme is different from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be used or the destination machine must be added to the TrustedHosts configuration setting."</td>
<td>Workgroup or non-Kerberos connection</td>
<td>HTTPS, or a specific TrustedHosts entry (Step 9)</td>
</tr>
</tbody></table>
<p>Two cases look like remoting failures and aren't this one. First, Access is denied after the session opens: the account isn't in Administrators or Remote Management Users on the target, or the endpoint's Permission excludes it (Step 5). Second, the second hop: "Access to the resource on ServerC is denied, because the credentials you used to create the PowerShell Remoting session aren't passed from ServerB to ServerC." That's delegation, and Microsoft's second-hop page lists the options in order of preference, starting with CredSSP and resource-based Kerberos constrained delegation.</p>
<hr />
<h2>Step 9: Change one link (a change)</h2>
<p>Fix the link the reads pointed at, on the side it's on, and nothing else. Write down the before value for each.</p>
<p><strong>Service stopped or disabled (target).</strong> Set it back to Microsoft's server default and start it:</p>
<pre><code class="language-powershell"># Change, on the target: WinRM to Automatic and running
Set-Service -Name WinRM -StartupType Automatic
Start-Service -Name WinRM
</code></pre>
<p>If a policy or baseline set it to Disabled, fix it there, or the next refresh puts it back (our reading).</p>
<p><strong>No listener, missing rules, or default endpoints removed (target).</strong> Enable-PSRemoting puts back Microsoft's defaults. What it does, per Microsoft: it runs Set-WSManQuickConfig, which "Starts the WinRM service.", "Sets the startup type on the WinRM service to Automatic.", "Creates a listener to accept requests on any IP address.", and "Enables a firewall exception for WS-Management communications.", then sets up and enables the session configurations and restarts WinRM.</p>
<pre><code class="language-powershell"># Change, on the target only, in an elevated Windows PowerShell window: restore the remoting defaults
Enable-PSRemoting -Force
</code></pre>
<p>Run it only on the target that needs it. Microsoft: "Because the configuration starts listeners, it's prudent to run it only where it's needed." Run it in Windows PowerShell if you manage Windows PowerShell endpoints; Microsoft warns that in PowerShell 7 it "only affects endpoints created with PowerShell version 6 and greater". <code>-SkipNetworkProfileCheck</code> does nothing on a server: Microsoft says it "has no effect on server versions of Windows, that by default, have a local subnet firewall rule for public networks."</p>
<p><strong>Listener functionality broken (target, error 0x80338012 on local <code>winrm</code> commands).</strong> Microsoft's two steps, in order:</p>
<pre><code class="language-console">winrm invoke Restore winrm/Config
winrm quickconfig
</code></pre>
<p><strong>Policy listener with an empty filter (GPO).</strong> Edit the listener policy in the GPO that applies to the target and fill in the IPv4 and IPv6 filters (Microsoft: "Wildcards ( *) are permitted."), or narrow them to the address ranges the servers use. Then read <code>Get-WSManInstance winrm/config/listener -Enumerate</code> again after the next policy refresh: ListeningOn should list the server's addresses.</p>
<p><strong>Adapter in the Public profile on a domain server (target).</strong> Fix why domain detection failed: DNS servers on that adapter, a DC reachable from that adapter's network, a second adapter that shouldn't be registering. Microsoft's Set-NetConnectionProfile can change Public to Private but not to Domain: "You cannot set the DomainAuthenticated type by using this cmdlet. The server automatically sets the value of DomainAuthenticated when the network is authenticated to a domain controller." For an adapter that truly isn't on a domain network (a backup or storage VLAN with no DC), Microsoft's form is:</p>
<pre><code class="language-powershell"># Change, on the target: mark one non-domain adapter Private (not a fix for a domain adapter that should be DomainAuthenticated)
Set-NetConnectionProfile -InterfaceAlias 'Ethernet 2' -NetworkCategory Private
</code></pre>
<p><strong>Rule scoped to the local subnet, and the clients really live elsewhere (target or GPO).</strong> Microsoft's example opens the Public rule to everywhere: <code>Set-NetFirewallRule -Name 'WINRM-HTTP-In-TCP-PUBLIC' -RemoteAddress Any</code>. Read its warning first: "The firewall rule for public networks is meant to protect the computer from potentially malicious external connection attempts. Use caution when removing this rule." We'd add only the management subnet instead (our form):</p>
<pre><code class="language-powershell"># Change, on the target: keep the local subnet and add only the admin or jump-box subnet to the Public WinRM rule
Set-NetFirewallRule -Name 'WINRM-HTTP-In-TCP-PUBLIC' -RemoteAddress LocalSubnet, 10.50.0.0/24
</code></pre>
<p>If the rule comes from a GPO, change it in the GPO; a local edit won't win.</p>
<p><strong>Proxy in the path (client).</strong> If the WinHTTP proxy isn't needed on that machine, Microsoft's reset is <code>netsh winhttp reset proxy</code>, which "Resets the WinHTTP proxy setting to DIRECT." If something else on the box needs it, leave it and bypass the proxy for remoting only (our form):</p>
<pre><code class="language-powershell"># Change, for this session only, on the client: no proxy for this remoting connection
$so = New-PSSessionOption -ProxyAccessType NoProxyServer
Enter-PSSession -ComputerName FS02.contoso.com -SessionOption $so
</code></pre>
<p><strong>IP address or workgroup target (client).</strong> Prefer connecting by name with Kerberos. If you must use NTLM, prefer HTTPS on the target; otherwise add the one host, not <code>*</code>. Microsoft: "The value that you set for the TrustedHosts item affects all users of the computer." Save the current value, then add to it:</p>
<pre><code class="language-powershell"># Read first, on the client: keep the current value somewhere you can restore it from
$curValue = (Get-Item WSMan:\localhost\Client\TrustedHosts).Value
$curValue

# Change, on the client: append one host to the existing list (Microsoft's -Concatenate form)
Set-Item WSMan:\localhost\Client\TrustedHosts -Value '10.20.30.40' -Concatenate
</code></pre>
<p>For Microsoft Entra-only joined machines, Microsoft's documented fix is a TrustedHosts value plus the client SPN prefix change: "To resolve this issue, set the default SPN prefix to HOST by running the following command". Apply that only to the Entra-only case it was written for.</p>
<p><strong>HTTPS instead of TrustedHosts (target).</strong> Microsoft: "WinRM HTTPS requires a local computer Server Authentication certificate with a CN matching the hostname to be installed. The certificate mustn't be expired, revoked, or self-signed." With the certificate in place, run <code>winrm quickconfig -transport:https</code> on the target, open TCP 5986 ("Open port 5986 for HTTPS transport to work."), and connect with <code>-UseSSL</code>.</p>
<p><strong>SPN on the wrong account (domain).</strong> If HTTP/FS02 is registered to a service account for a web app, either move that app to its own name, or use <code>New-PSSessionOption -IncludePortInSPN</code> for remoting so the client asks for HTTP/FS02:5985. Moving SPNs breaks the other service if you get it wrong; <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> covers reading SPNs before changing them. Microsoft's <code>setspn</code> note: "you should use setspn -S instead because it verifies that there are no duplicate SPNs."</p>
<hr />
<h2>Step 10: Prove it from the same client (read-only, and again after a reboot)</h2>
<p>Run the same reads from the client that failed, by the name users type, then again after the target reboots and after the next policy refresh. A profile that drops to Public at boot, or a GPO that turns the rule back off, only shows up then:</p>
<pre><code class="language-powershell"># Read-only, on the client: the path, the WinRM answer, and a real remote command
Test-NetConnection -ComputerName FS02.contoso.com -Port 5985
Test-WSMan -ComputerName FS02.contoso.com -Authentication Default
Invoke-Command -ComputerName FS02.contoso.com -ScriptBlock { $PSVersionTable.PSVersion }

# Read-only, on the client: TrustedHosts holds only what you meant to add
Get-Item WSMan:\localhost\Client\TrustedHosts

# Read-only, on the target after a reboot: still the right profile, rule, and listener
Get-NetConnectionProfile | Select-Object -Property InterfaceAlias, NetworkCategory
Get-NetFirewallRule -Name 'WINRM*' | Select-Object -Property Name, Enabled, Profile, Action
Get-WSManInstance winrm/config/listener -Enumerate
</code></pre>
<p>If the tool was Windows Admin Center, run the <code>Invoke-Command</code> from the gateway; if it was Server Manager, refresh the server in the pool and read its Manageability column.</p>
<p>Pass criteria (our reading): <code>TcpTestSucceeded</code> True on the port you use from every client subnet that needs it; <code>Test-WSMan -Authentication Default</code> returns the product version; <code>Invoke-Command</code> by the FQDN users type returns output without <code>-Credential</code> inside the domain; the adapter is DomainAuthenticated (or deliberately Private for a non-domain network) after a reboot; the WinRM rule for that profile is enabled with a scope you chose; ListeningOn lists the server's addresses; TrustedHosts is empty or holds specific entries with a written reason; and the original command works from the original client.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it doesn't help (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Setting TrustedHosts to <code>*</code></td>
<td>It doesn't change reachability, and Microsoft: "The computers in the trusted hosts list aren't authenticated." It affects every user on the client</td>
</tr>
<tr>
<td>Adding the server to TrustedHosts again</td>
<td>Microsoft's workgroup rows with TrustedHosts already set still fail on the subnet restriction; the fix is the rule scope</td>
</tr>
<tr>
<td>Running <code>Enable-PSRemoting -SkipNetworkProfileCheck</code> on a server</td>
<td>Microsoft: the parameter "has no effect on server versions of Windows"</td>
</tr>
<tr>
<td>Opening the Public rule to Any on every server</td>
<td>Microsoft's warning: the rule "is meant to protect the computer from potentially malicious external connection attempts." Fix the profile, or add one subnet</td>
</tr>
<tr>
<td>Running <code>winrm quickconfig</code> and assuming every profile is covered</td>
<td>Microsoft: it "creates a firewall exception only for the current user profile."</td>
</tr>
<tr>
<td>Restarting the WinRM service on the client</td>
<td>The client doesn't need a listener; the missing answer is on the target or the path</td>
</tr>
<tr>
<td>Editing a local rule or listener that a GPO owns</td>
<td>The next policy refresh puts the GPO's version back; read RSOP and <code>Source : GPO</code> first</td>
</tr>
<tr>
<td>Treating ping as the test</td>
<td>ICMP and TCP 5985 are filtered separately; <code>Test-NetConnection -Port 5985</code> is the test</td>
</tr>
<tr>
<td>Switching to an IP address to get around a DNS problem</td>
<td>That moves you from Kerberos to NTLM and into the TrustedHosts case; fix the name</td>
</tr>
<tr>
<td>Disabling the Windows Firewall to test</td>
<td>It hides which rule or profile was wrong, and someone forgets to turn it back on</td>
</tr>
<tr>
<td>Setting LocalAccountTokenFilterPolicy for a reachability error</td>
<td>Microsoft: it "disables user account control (UAC) remote restrictions for all users of all affected computers." It's for local-admin access, not for "cannot complete"</td>
</tr>
</tbody></table>
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>WinRM disabled on the target</td>
<td>Automatic and Running, and fix the policy or baseline that disabled it</td>
<td>Starting it by hand on every reboot</td>
</tr>
<tr>
<td>No listener or missing default rules</td>
<td><code>Enable-PSRemoting -Force</code> on that target only</td>
<td>Running it on every machine in the estate just in case</td>
</tr>
<tr>
<td>Policy listener with ListeningOn empty</td>
<td>Fill in the listener policy's IPv4 and IPv6 filters in the GPO</td>
<td>Local <code>winrm</code> edits the GPO overwrites</td>
</tr>
<tr>
<td>Domain server in the Public profile</td>
<td>Fix domain detection (DNS, DC path, extra adapter)</td>
<td>Opening the Public rule to Any</td>
</tr>
<tr>
<td>Admins in another subnet blocked by the local-subnet scope</td>
<td>Add the management subnet to the rule scope, in the GPO if it's policy-managed</td>
<td><code>-RemoteAddress Any</code> on every server</td>
</tr>
<tr>
<td>Network firewall between sites drops 5985</td>
<td>A rule for 5985 (or 5986) from the admin subnets to the servers</td>
<td>Moving admins onto the server subnet</td>
</tr>
<tr>
<td>WinHTTP proxy left by a tool</td>
<td>Reset it if nothing needs it, or <code>-ProxyAccessType NoProxyServer</code> for remoting</td>
<td>Leaving it and routing WinRM through the proxy</td>
</tr>
<tr>
<td>Workgroup, Entra-only, or IP-only targets</td>
<td>HTTPS listener with a proper certificate, or a specific TrustedHosts entry</td>
<td>TrustedHosts <code>*</code></td>
</tr>
<tr>
<td>HTTP SPN registered to a service account</td>
<td>Move the app's name, or <code>-IncludePortInSPN</code> for remoting</td>
<td>Deleting the SPN without reading who uses it</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does "WinRM cannot complete the operation" mean?</strong>
The client sent a WS-Management request to that name and port and got no usable answer. Microsoft's text names the checks: "Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the Windows Remote Management service is enabled and allows access from this computer." It's reachability, not authentication; the authentication errors say "cannot process the request" instead (Steps 2 to 6).</p>
<p><strong>What port does WinRM use?</strong>
TCP 5985 for HTTP and TCP 5986 for HTTPS. Microsoft: "Starting in WinRM 2.0, the default listener ports configured by Winrm quickconfig are port 5985 for HTTP transport, and port 5986 for HTTPS." Older Windows used 80 and 443 (Step 3).</p>
<p><strong>How do I fix "the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet"?</strong>
Read the target's network profile first. On a domain server, Public usually means domain detection failed on that adapter; fix that and the Domain rule applies. If clients in another subnet really need access on a Public network, add their subnet to the Public WinRM rule rather than opening it to Any (Steps 6 and 9).</p>
<p><strong>Should I set TrustedHosts to * to fix WinRM?</strong>
No. TrustedHosts is for NTLM connections by IP address or to workgroup machines, and Microsoft describes it as the list of hosts "for which you wish to suppress the error generated by being unable to verify the server's identity." It doesn't fix a firewall, profile, or listener problem. When you need it, add specific hosts with <code>-Concatenate</code>, or use HTTPS (Steps 8 and 9).</p>
<p><strong>Why does Enter-PSSession work by name but not by IP address?</strong>
By name inside a domain, remoting uses Kerberos. Microsoft: "Kerberos authentication doesn't support IP addresses. When you specify an IP address, NTLM authentication is used." NTLM needs HTTPS or a TrustedHosts entry on the client, plus <code>-Credential</code> (Step 8).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> (when the error turns into a Kerberos or SPN failure once the connection works)</li>
<li><a href="https://dev.to/adminpackstudio/hyper-v-live-migration-failed-event-21502-a-read-first-switch-processor-version-and-kerberos-181f">Hyper-V Live Migration Failed (Event 21502)</a> (when remoting fails between Hyper-V hosts during migration or remote host management)</li>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (when the same firewall, profile, or NIC also blocks RPC between the same servers)</li>
</ul>
<h2>Free checklist</h2>
<p>Setting TrustedHosts to <code>*</code> on every admin box, or opening the Public WinRM rule to Any on every server, before anyone read which link dropped the request, is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=winrm-cannot-complete-the-operation">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=winrm-cannot-complete-the-operation</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=winrm-cannot-complete-the-operation">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=winrm-cannot-complete-the-operation</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 and 10 are read-only checks: <code>Resolve-DnsName</code>, <code>Test-NetConnection</code>, <code>Get-NetIPAddress</code>, <code>Test-WSMan</code>, <code>Get-Service</code>, <code>winrm enumerate</code>, <code>winrm get</code>, <code>Get-WSManInstance</code>, <code>Get-PSSessionConfiguration</code>, <code>Get-NetConnectionProfile</code>, <code>Get-NetFirewallRule</code>, <code>Get-NetFirewallAddressFilter</code>, <code>Get-NetFirewallPortFilter</code>, <code>netsh winhttp show advproxy</code>, <code>netsh winhttp show proxy</code>, <code>Get-Item</code> on the WSMan: drive, <code>setspn -Q</code>, <code>setspn -L</code>, <code>klist get</code>, and <code>Invoke-Command</code> with a read-only script block. Setting the WinRM service startup type, starting it, running <code>Enable-PSRemoting</code>, <code>winrm invoke Restore</code>, or <code>winrm quickconfig</code>, changing listener policy, network profiles, firewall rule scope, the WinHTTP proxy, TrustedHosts, HTTPS listeners and certificates, the SPN prefix, or SPNs are deliberate changes. Change one link at a time, prove it from the same client after a reboot, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Failover Cluster Event ID 1135 (Node Removed from Active Membership): A Read-First Heartbeat, Network, and Threshold Triage]]></title><description><![CDATA[Cluster Event 1135, node removed from membership? Read which nodes logged it, the cluster networks, the heartbeat values, and the cluster log first.]]></description><link>https://adminpackstudio.hashnode.dev/failover-cluster-event-id-1135-node-removed-from-active-membership-a-read-first-heartbeat-network-and-threshold-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/failover-cluster-event-id-1135-node-removed-from-active-membership-a-read-first-heartbeat-network-and-threshold-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[networking]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 16:47:50 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Overnight, during the backup window, HV02 dropped out of the Hyper-V cluster for about a minute. The System log on HV01 and HV03 has Event 1135: 'Cluster node 'HV02' was removed from the active failover cluster membership.' A few VMs restarted on other hosts. It happened last Tuesday too. A forum post says to raise SameSubnetThreshold to the maximum so it stops. Can we do that?"</p>
<p>Not yet. <strong>Event 1135 isn't a diagnosis. It's the cluster telling you the other nodes stopped hearing a node's heartbeats on every route for longer than the threshold allows, or that its Cluster service stopped. The cause is usually below the cluster: a NIC, a driver, a team or virtual switch, a physical or virtual switch, a firewall or IPsec policy change, a network profile flip, packet loss under load, or real latency between sites.</strong> Microsoft's event text says as much: the node "was removed from the active failover cluster membership. The Cluster service on this node may have stopped. This could also be due to the node having lost communication with other active nodes in the failover cluster." It's just as plain about the forum fix: "Increasing these values to their maximum might make the event and node removal go away, it just masks the problem. It doesn't fix anything." Work through it as a chain: <strong>which node, which nodes logged it, and when</strong> → <strong>which neighbor events came with it?</strong> → <strong>is the node up, isolated, or quarantined, and was the Cluster service running?</strong> → <strong>which cluster networks carry heartbeats, and are there two?</strong> → <strong>what are the heartbeat values on this cluster, really?</strong> → <strong>what does the cluster log say about routes at that second?</strong> → <strong>what did the NIC, driver, team, and switch see?</strong> → <strong>did a firewall, IPsec, profile, or antivirus change block it?</strong> → <strong>change one link</strong> → <strong>prove it stays quiet.</strong> <code>Get-WinEvent</code>, <code>Get-ClusterNode</code>, <code>Get-ClusterNetwork</code>, <code>Get-ClusterNetworkInterface</code>, <code>Get-Cluster</code>, <code>Get-ClusterLog</code>, <code>Get-NetAdapter</code>, <code>Get-NetAdapterStatistics</code>, <code>Get-Counter</code>, <code>Get-NetConnectionProfile</code>, <code>Get-NetFirewallRule</code>, and <code>Test-Cluster</code> are the sources for that chain. Raise the thresholds "to be safe" and the next hard failure takes longer to detect, while the dropped packets that caused this one are still dropping.</p>
<p>If the 1135s line up with live migrations, <a href="https://dev.to/adminpackstudio/hyper-v-live-migration-failed-event-21502-a-read-first-switch-processor-version-and-kerberos-181f">Hyper-V Live Migration Failed (Event 21502)</a> covers the migration network and virtual switch side. If they line up with the backup window and checkpoints fail too, <a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed</a> covers the VSS and storage side. This post covers <strong>"a Windows Server failover cluster (Hyper-V, SQL Server, file server, or any other role) that logs FailoverClustering Event 1135 (cluster node was removed from the active failover cluster membership), with or without 1177 quorum loss, 1069 resource failures, VMs restarting or going unmonitored, or a node that ends up isolated or quarantined, and you need a read-first path before you raise heartbeat thresholds, evict the node, or rebuild networking."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Read 1135 on every node, not one.</strong> Microsoft: "This event is logged on all nodes in the Cluster except for the node that was removed." Which nodes logged which names tells you whether one node, one site link, or the whole network was the problem (Steps 1 and 2).</li>
<li><strong>A node is removed only when all its routes are down.</strong> Microsoft's heartbeat article: "If all routes are marked down for W2K8-R2-NODE2, it's removed from active failover cluster membership and the Event 1135 that you see in the first section is logged." One working cluster network keeps a node in; that's why you want two (Step 4).</li>
<li><strong>Heartbeats are UDP 3343.</strong> Microsoft: "IPv6 is the default protocol that Failover Clustering will use for its heartbeats. The heartbeat itself is a UDP unicast network packet that communicates over Port 3343." Anything that drops, delays, or blocks that traffic for long enough removes the node (Steps 7 and 8).</li>
<li><strong>Read the thresholds before you change them.</strong> Defaults differ by Windows Server version and by Microsoft page, so <code>Get-Cluster | Format-List *subnet*</code> is the only number that counts for your cluster (Step 5).</li>
<li><strong>The cluster log has the route history.</strong> <code>Get-ClusterLog -TimeSpan</code> with <code>-UseLocalTime</code> shows which route was marked down and when; by default its timestamps are GMT (Step 6).</li>
<li><strong>Raising thresholds is a trade, not a fix.</strong> Microsoft: "cranking up the thresholds to high values does not fix nor resolve the transient network issue, it simply masks the problem by making health monitoring less sensitive." (Steps 5 and 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which node was removed, which nodes logged it, the exact times, the cluster's role and platform, and what was running at that moment</td>
<td>"HV02 keeps falling out of the cluster" with no times and no list of which node logged what</td>
</tr>
<tr>
<td>2. Event read</td>
<td>Every 1135 on every node is collected, with 1006, 1069, 1127, 1129, 1130, 1177, 1592, 1554, and the isolation and quarantine events around it</td>
<td>One node's System log read; the pattern across nodes never seen</td>
</tr>
<tr>
<td>3. Node state</td>
<td>You know whether the node is Up, Down, Paused, isolated, or quarantined, and whether its Cluster service stopped or the network went quiet</td>
<td>A quarantined node that "won't rejoin" treated as a new fault</td>
</tr>
<tr>
<td>4. Cluster networks</td>
<td>At least two networks allow cluster communication, each is Up on every node, and you know which carries cluster, live migration, and client traffic</td>
<td>One network for everything; a network partitioned or down on one node; live migration on the heartbeat network</td>
</tr>
<tr>
<td>5. Heartbeat values</td>
<td>The delay, threshold, and route history values on this cluster are read, not assumed</td>
<td>A forum's defaults quoted for the wrong Windows version; thresholds already at the maximum</td>
</tr>
<tr>
<td>6. Cluster log</td>
<td>The route-down lines for the time of the 1135 are found in local time</td>
<td>System log in local time compared with a cluster log in GMT, an hour or more apart</td>
</tr>
<tr>
<td>7. NIC and switch</td>
<td>Drivers, firmware, teaming, virtual switch, and receive buffers are known, and discarded packets are counted</td>
<td>Old NIC drivers; VMXNET3 buffers at defaults; discards climbing under backup load</td>
</tr>
<tr>
<td>8. Firewall and policy</td>
<td>UDP 3343 is allowed, the network profile stays Domain, IPsec refreshes and antivirus filters are accounted for</td>
<td>A profile flip to Public, an IPsec policy refresh, or a new filter driver at the time of the 1135</td>
</tr>
<tr>
<td>9. Change</td>
<td>One link fixed, with the node drained first if you touch it</td>
<td>Drivers, thresholds, teaming, and firewall all changed in one maintenance window</td>
</tr>
<tr>
<td>10. Proof</td>
<td>No new 1135 through the same load window, routes stay up in the cluster log, and validation is clean</td>
<td>Quiet for a week until the next backup or patch night</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which node was removed, and which nodes logged 1135 for it</strong></td>
<td>Microsoft's three scenarios are told apart only by comparing every node's events (Step 2)</td>
</tr>
<tr>
<td><strong>The exact time of each 1135, to the second</strong></td>
<td>You'll match it to the cluster log, the NIC driver events, and the network profile log (Steps 6 to 8)</td>
</tr>
<tr>
<td><strong>The cluster's role and platform</strong></td>
<td>Hyper-V hosts, guest clusters on VMware, SQL Server in Azure IaaS, and stretched clusters each have their own usual cause (Steps 5 and 7)</td>
</tr>
<tr>
<td><strong>Sites and subnets</strong></td>
<td>Same-subnet and cross-subnet heartbeats use different values, and a site link failure looks like Microsoft's Scenario B (Steps 2 and 5)</td>
</tr>
<tr>
<td><strong>What was running at that moment</strong></td>
<td>Backups, live migrations, patching, a switch or firewall change, a Group Policy refresh, an antivirus update</td>
</tr>
<tr>
<td><strong>How often, and since when</strong></td>
<td>Once after a switch reboot is a different case from every night at 01:00, and both differ from three times in an hour (quarantine, Step 3)</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Every other node logged 1135 for the same node</td>
<td>Microsoft's Scenario A: "This quite suggests that at the time of issue, either due to network congestion or otherwise the communication to the NODE A was lost."</td>
<td>Steps 7 and 8 on that node</td>
</tr>
<tr>
<td>Nodes in one site logged 1135 for the nodes in the other site, and the other way round</td>
<td>Microsoft's Scenario B: "it is indicative that there was a communication failure, over the link that connects these sites."</td>
<td>Steps 4 and 5</td>
</tr>
<tr>
<td>Each node logged a different set of names</td>
<td>Microsoft's Scenario C: "Such events are possible when the network channels between the nodes are choked and the cluster communication messages are not reaching in a timely manner"</td>
<td>Steps 4, 6, and 7</td>
</tr>
<tr>
<td>1135 lines up with backups or live migrations</td>
<td>Load on a shared network or host; heartbeats delayed or dropped while it runs</td>
<td>Steps 4 and 7</td>
</tr>
<tr>
<td>1135 lines up with a Group Policy refresh or a firewall change</td>
<td>Microsoft names IPsec policy refreshes and profile changes among the causes</td>
<td>Step 8</td>
</tr>
<tr>
<td>The removed node's own log shows its Cluster service stopped or crashed first</td>
<td>The node didn't lose the network; the service went away</td>
<td>Step 3</td>
</tr>
<tr>
<td>1177 on the remaining nodes as well</td>
<td>Quorum was lost, so the whole cluster stopped, not only one node</td>
<td>Steps 2 and 3</td>
</tr>
<tr>
<td>The node is quarantined and won't rejoin</td>
<td>It left the cluster too many times in an hour; that's a result of 1135, not a new fault</td>
<td>Steps 3 and 9</td>
</tr>
<tr>
<td>Guest cluster on VMware</td>
<td>Microsoft's VMware article: VMXNET3 dropping inbound packets</td>
<td>Step 7</td>
</tr>
<tr>
<td>Cluster nodes in Azure IaaS</td>
<td>Microsoft's Azure thresholds article: the defaults "don't consider the possibility of induced latency caused by a multitenant environment"</td>
<td>Step 5</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the events on every node (read-only)</h2>
<p>1135 is in the <strong>System</strong> log, from the <strong>FailoverClustering</strong> source. Microsoft's guidance is to collect them all: "Review the Event ID 1135 that you're seeing on the nodes and copy all the instances of this event." Read every node in one pass, with the events that usually come with it:</p>
<pre><code class="language-powershell"># Read-only: FailoverClustering events of interest on every node, last 7 days, oldest first
$ids   = 1006, 1069, 1127, 1129, 1130, 1135, 1146, 1177, 1554, 1592, 1653, 1672, 1673, 1681
$since = (Get-Date).AddDays(-7)
(Get-ClusterNode).Name | ForEach-Object -Process {
    $n = $_
    Get-WinEvent -ComputerName $n -FilterHashtable @{
        LogName      = 'System'
        ProviderName = 'Microsoft-Windows-FailoverClustering'
        Id           = $ids
        StartTime    = $since
    } -ErrorAction SilentlyContinue |
        Select-Object @{n='LoggedOn';e={$n}}, TimeCreated, Id, Message
} | Sort-Object TimeCreated | Format-Table -AutoSize -Wrap
</code></pre>
<p>Then build the who-logged-what grid (our form): one row per 1135, with the node that logged it and the node named in the message. If every row names the same node, it's Scenario A; if the names split by site, B; if they're scattered, C.</p>
<pre><code class="language-powershell"># Read-only: which node logged 1135 for which node, last 7 days
(Get-ClusterNode).Name | ForEach-Object -Process {
    $n = $_
    Get-WinEvent -ComputerName $n -FilterHashtable @{ LogName = 'System'; Id = 1135; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
        Select-Object @{n='LoggedOn';e={$n}}, TimeCreated, @{n='Removed';e={ ($_.Message -split "'")[1] }}
} | Sort-Object TimeCreated
</code></pre>
<p>Read the result against Microsoft's text for each event:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Symbolic name</th>
<th>Microsoft's message (excerpt)</th>
<th>What it tells you (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>1135</td>
<td>EVENT_NODE_DOWN</td>
<td>"was removed from the active failover cluster membership. The Cluster service on this node may have stopped. This could also be due to the node having lost communication with other active nodes in the failover cluster."</td>
<td>The node lost all routes, or its service stopped; the rest of this post</td>
</tr>
<tr>
<td>1127</td>
<td>TM_EVENT_CLUSTER_NETINTERFACE_FAILED</td>
<td>"Cluster network interface '%1' for cluster node '%2' on network '%3' failed."</td>
<td>One adapter on one node failed; the start of a route loss (Steps 4 and 7)</td>
</tr>
<tr>
<td>1129</td>
<td>TM_EVENT_CLUSTER_NETWORK_PARTITIONED</td>
<td>"Some attached failover cluster nodes cannot communicate with each other over the network."</td>
<td>One cluster network is split; others may still be carrying heartbeats (Step 4)</td>
</tr>
<tr>
<td>1130</td>
<td>TM_EVENT_CLUSTER_NETWORK_DOWN</td>
<td>"None of the available nodes can communicate using this network."</td>
<td>A whole cluster network is down; a switch or VLAN, not a node (Step 7)</td>
</tr>
<tr>
<td>1592</td>
<td>CLUSSVC_TCP_RECONNECT_CONNECTION_REESTABLISHED</td>
<td>"Network communication was reestablished. This could be due to communication temporarily being blocked by a firewall or connection security policy update."</td>
<td>A short break that recovered; look for the same cause as the 1135 (Step 8)</td>
</tr>
<tr>
<td>1006</td>
<td>NM_EVENT_MEMBERSHIP_HALT</td>
<td>"Cluster service was halted due to incomplete connectivity with other cluster nodes."</td>
<td>The node's own side: its service halted on connectivity</td>
</tr>
<tr>
<td>1554</td>
<td>SERVICE_NETWORK_CONNECTIVITY_LOST</td>
<td>"This cluster node has lost all network connectivity. It cannot participate in the cluster until connectivity is restored."</td>
<td>The node lost every network, not only the heartbeat path</td>
</tr>
<tr>
<td>1177</td>
<td>MM_EVENT_ARBITRATION_FAILED</td>
<td>"The Cluster service is shutting down because quorum was lost."</td>
<td>Enough nodes or the witness were lost that the cluster stopped (Step 3)</td>
</tr>
<tr>
<td>1069</td>
<td>RCM_RESOURCE_FAILURE</td>
<td>"Cluster resource '%1' in clustered role '%2' failed."</td>
<td>A consequence: a resource failed during or after the removal</td>
</tr>
<tr>
<td>1146</td>
<td>RCM_EVENT_RESMON_DIED</td>
<td>"The cluster Resource Hosting Subsystem (RHS) process was terminated and will be restarted."</td>
<td>Resource health detection, not the network; read which resource</td>
</tr>
<tr>
<td>1653</td>
<td>SERVICE_NO_CONNECTIVITY</td>
<td>"failed to join the cluster because it could not communicate over the network with any other node in the cluster."</td>
<td>The node tried to come back and still can't reach anyone (Step 8)</td>
</tr>
<tr>
<td>1673</td>
<td>CLUSTER_NODE_ENTERED_ISOLATED_STATE</td>
<td>"Cluster node '%1' has entered the isolated state."</td>
<td>Windows Server 2016 and later: the node left membership but still hosts its VMs (Step 3)</td>
</tr>
<tr>
<td>1681</td>
<td>CLUSTER_GROUPS_UNMONITORED_ON_NODE</td>
<td>"Virtual machines on node '%1' have entered an unmonitored state."</td>
<td>VMs kept running on the isolated node, unmonitored</td>
</tr>
<tr>
<td>1672</td>
<td>EVENT_NODE_QUARANTINED</td>
<td>"Cluster node '%1' has been quarantined."</td>
<td>It left too often; it stays out until the quarantine expires or you clear it (Steps 3 and 9)</td>
</tr>
</tbody></table>
<p>Two details matter when you compare with forum posts. First, read the removed node's own System log for the same minute: Microsoft's heartbeat article says that on the removed node "the Cluster Service is terminated and then restarted so it can try to rejoin the Cluster", so service stop and start events there are expected after the 1135, not before it. A service that stopped <em>before</em> the others logged 1135 is a different case (Step 3). Second, Microsoft's 1135 article lists the companion events it expects: 1069 for resources, "Event ID 1177 for Quorum loss", and "Event ID 1006 for Cluster service halted". If 1177 is in the list, the whole cluster went down, and that changes the urgency.</p>
<hr />
<h2>Step 3: Is the node up, isolated, or quarantined? (read-only)</h2>
<p>Read node state and the Cluster service before you read the network:</p>
<pre><code class="language-powershell"># Read-only: name, id, and state of each node
Get-ClusterNode

# Read-only: the Cluster service on every node
Invoke-Command -ComputerName (Get-ClusterNode).Name -ScriptBlock { Get-Service -Name ClusSvc } |
    Select-Object PSComputerName, Name, Status, StartType

# Read-only: Microsoft's check that the cluster answers (Windows Server 2012 and later)
Get-ClusterResource

# Read-only: isolation and quarantine settings on this cluster
Get-Cluster | Format-List -Property Quarantine*, Resiliency*
</code></pre>
<p>Microsoft's 1135 article checks the service early: "Is the cluster service continuously running and available on all the nodes?" Its firewall section gives the service name, "System service name: ClusSvc".</p>
<p>On Windows Server 2016 and later, a node that loses membership on a cluster with VMs may not go straight to Down. Microsoft's VM compute resiliency article describes two states:</p>
<table>
<thead>
<tr>
<th>State</th>
<th>Microsoft's description</th>
<th>What to do (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>Isolated</td>
<td>"The node is no longer in an active membership" and "The node continues to host the VM role"</td>
<td>Read the network now; it may rejoin on its own</td>
</tr>
<tr>
<td>Down after isolation</td>
<td>"after a certain period (default of 4 minutes), the VM is failed over to a suitable node in the cluster, and the node is now moved to a Down state."</td>
<td>The VMs moved; read why the node didn't come back</td>
</tr>
<tr>
<td>Quarantined</td>
<td>"The node is no longer allowed to join the cluster for a fixed time period (default: 2 hours)"</td>
<td>Find the cause before you clear it (Step 9)</td>
</tr>
</tbody></table>
<p>Microsoft's numbers for quarantine: "By default, a node is quarantined, if it ungracefully leaves the cluster, three times within an hour", and "No more than 25% of nodes can be quarantined at any given time". The <code>Get-Cluster</code> reference agrees: QuarantineThreshold "is set to 3 by default", and QuarantineDuration is "set to 7200 seconds or 2 hours by default". Microsoft also notes what isolation does to storage: for block storage (FC, FCoE, iSCSI, SAS) "The VM is placed in the Paused Critical state." while SMB-backed VMs keep running.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>All nodes Up now, ClusSvc Running everywhere</td>
<td>It recovered; the evidence is in the logs, not the current state (Steps 6 to 8)</td>
</tr>
<tr>
<td>One node Down, ClusSvc Running on it</td>
<td>It's trying to join and can't; look for 1653 or 1572 on it (Step 8)</td>
</tr>
<tr>
<td>ClusSvc stopped first on the removed node, before the 1135 on the others</td>
<td>A service or host problem, not a network drop; read that node's System and Application logs for the minute before</td>
</tr>
<tr>
<td>Node Paused</td>
<td>Someone drained or paused it; that's not a 1135 cause</td>
</tr>
<tr>
<td>Node quarantined</td>
<td>Event 1672 tells you until when; Microsoft's text gives the way out, "Start-ClusterNode -ClearQuarantine"</td>
</tr>
<tr>
<td>1177 on the survivors</td>
<td>Quorum was lost; read the witness and node votes before anything else (<code>Get-ClusterQuorum</code>)</td>
</tr>
</tbody></table>
<p>For the quorum branch, read the configuration once:</p>
<pre><code class="language-powershell"># Read-only: quorum type and witness for the local cluster
Get-ClusterQuorum
</code></pre>
<p>Microsoft's quorum guidance is short: "We recommend you always configure a witness if you're using Windows Server 2012 R2 and later." A two-node cluster with no witness that loses one node's heartbeats is the case where a single 1135 turns into an outage (our reading).</p>
<hr />
<h2>Step 4: Which cluster networks carry heartbeats? (read-only)</h2>
<p>Microsoft's heartbeat article explains why the number of networks matters: each node tracks every route to every other node, and "If other routes are still considered to be up, W2K8-R2-NODE2 will remain as an active member." A node is removed only when the last route goes. Read the networks, their roles, and their metrics:</p>
<pre><code class="language-powershell"># Read-only: cluster networks, their state, role, and metric
Get-ClusterNetwork | Format-Table -Property Name, State, Role, Address, Metric, AutoMetric -AutoSize

# Read-only: every node's adapter on every cluster network, and its state
Get-ClusterNetworkInterface | Sort-Object Network, Node | Format-Table -AutoSize
</code></pre>
<p>Read the Role column against Microsoft's values for the network Role property:</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>Microsoft's name</th>
<th>Microsoft's description</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>ClusterNetworkRoleNone</td>
<td>"The network is not used by the cluster."</td>
</tr>
<tr>
<td>1</td>
<td>ClusterNetworkRoleInternalUse</td>
<td>"The network is used to carry internal cluster communication."</td>
</tr>
<tr>
<td>3</td>
<td>ClusterNetworkRoleInternalAndClient</td>
<td>"The network is used to connect client systems and to carry internal cluster communication."</td>
</tr>
</tbody></table>
<p>Then read the metrics. Microsoft's Metric property: "the network path with the lowest sum of metrics will be used." Microsoft's network prioritization article explains what that means for traffic: the network with the lowest metric carries cluster and CSV traffic, and the second lowest carries live migration traffic. It also explains why live migration on the heartbeat network hurts: "During a live migration, large chunks of memory are copied from one server to another as quickly as possible." and "For this reason it is recommended to have a dedicated network for live migration traffic so that it does not interfere with other important network traffic."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Only one network with Role 1 or 3</td>
<td>Every heartbeat route depends on one path; Microsoft's validation warns about this single point of failure (Step 9)</td>
</tr>
<tr>
<td>Two or more networks with Role 1 or 3, all Up</td>
<td>Good; a 1135 then means every path failed at once, which points at something shared (a host, team, switch, or policy)</td>
</tr>
<tr>
<td>A network Partitioned, or one node's interface Failed or Unreachable</td>
<td>That's the route that went; read Step 7 on that node's adapter</td>
</tr>
<tr>
<td>A network Down on every node</td>
<td>A switch, VLAN, or physical link; Event 1130 (Step 7)</td>
</tr>
<tr>
<td>The live migration network has the lowest metric, or the same network carries cluster, live migration, and backup traffic</td>
<td>Load on that network can delay heartbeats; compare 1135 times with migrations and backups</td>
</tr>
<tr>
<td>AutoMetric False on a network</td>
<td>Someone set the metric by hand; read why before you change it</td>
</tr>
<tr>
<td>An iSCSI network with Role 1 or 3</td>
<td>Microsoft's prioritization article recommends "Do not allow cluster network communication on this network" for storage traffic</td>
</tr>
</tbody></table>
<p>Microsoft's validation page gives an alternative to separate networks: "you can connect your cluster nodes with one network that is constructed with teamed network adapters, redundant switches, redundant routers, or similar hardware that removes single points of failure." If that's your design, the team and the switches are what you read in Step 7.</p>
<hr />
<h2>Step 5: What are the heartbeat values on this cluster? (read-only)</h2>
<p>Two kinds of setting decide how long the cluster waits. Microsoft's definitions: Delay "defines the frequency at which cluster heartbeats are sent between nodes", and Threshold "defines the number of heartbeats, which are missed before the cluster takes recovery action." Read the values this cluster actually has:</p>
<pre><code class="language-powershell"># Read-only: Microsoft's form for the heartbeat values
Get-Cluster | Format-List *subnet*

# Read-only: the same, plus cross-site values (Windows Server 2016 and later) and route history
Get-Cluster | Format-List -Property SameSubnet*, CrossSubnet*, CrossSite*, RouteHistoryLength
</code></pre>
<p>Don't compare them with a number from a forum. Microsoft's own pages give different defaults depending on the Windows Server version they describe:</p>
<table>
<thead>
<tr>
<th>Property</th>
<th>Microsoft's property reference (minimum, maximum, default)</th>
<th>Microsoft's tuning article (Windows Server 2012 R2, 2016, maximum)</th>
</tr>
</thead>
<tbody><tr>
<td>SameSubnetDelay</td>
<td>250, 2000, 1000 (milliseconds)</td>
<td>1 second, 1 second, 2 seconds</td>
</tr>
<tr>
<td>SameSubnetThreshold</td>
<td>3, 120, 5</td>
<td>5 heartbeats, 10 heartbeats, 120 heartbeats</td>
</tr>
<tr>
<td>CrossSubnetDelay</td>
<td>250, 4000, 1000 (milliseconds)</td>
<td>1 second, 1 second, 4 seconds</td>
</tr>
<tr>
<td>CrossSubnetThreshold</td>
<td>3, 120, 5</td>
<td>5 heartbeats, 20 heartbeats, 120 heartbeats</td>
</tr>
<tr>
<td>CrossSiteDelay</td>
<td>(not on the reference pages fetched)</td>
<td>NA, 1 second, 4 seconds</td>
</tr>
<tr>
<td>CrossSiteThreshold</td>
<td>(not on the reference pages fetched)</td>
<td>NA, 20 heartbeats, 120 heartbeats</td>
</tr>
<tr>
<td>RouteHistoryLength</td>
<td>0, 40, 10</td>
<td>"By default the RouteHistoryLength setting is set 10, which is two times the number of default thresholds."</td>
</tr>
</tbody></table>
<p>The property reference pages list Windows Server 2012 as the minimum supported server. Microsoft's Azure IaaS article states the newer default: "By default, Windows Server sets the SameSubnetThreshold to 10 and SameSubnetDelay to 1000 ms." The tuning article adds a version trap: "If the Hyper-V role is installed on a Windows Server 2012 R2 Failover Cluster, the SameSubnetThreshold default will automatically be increased to 10 and the CrossSubnetThreshold default will automatically be increased to 20." Older Microsoft pages (the heartbeat article, whose examples use Windows Server 2008 R2 node names, and the Windows Server 2008 multi-site heartbeat page) still show thresholds of 5 with a range of 3 to 10. That's why the read above is the step that matters.</p>
<p>Microsoft's own descriptions of each value:</p>
<table>
<thead>
<tr>
<th>Property</th>
<th>Microsoft's description</th>
</tr>
</thead>
<tbody><tr>
<td>SameSubnetDelay</td>
<td>"Controls the delay, in milliseconds, between netft heartbeats. This property applies to routes on the same network subnet."</td>
</tr>
<tr>
<td>SameSubnetThreshold</td>
<td>"Controls how many heartbeats can be missed on the same subnet before the route is declared as unreachable."</td>
</tr>
<tr>
<td>CrossSubnetDelay</td>
<td>"Controls the time interval, in milliseconds, that the cluster network driver waits between sending Cluster Service heartbeats across subnets."</td>
</tr>
<tr>
<td>CrossSubnetThreshold</td>
<td>"Controls how many Cluster Service heartbeats can be missed across subnets before it determines that Cluster Service has stopped responding."</td>
</tr>
<tr>
<td>RouteHistoryLength</td>
<td>"The number of heartbeats to log after a cluster network failure."</td>
</tr>
</tbody></table>
<p>Then do the math (Microsoft's): "both the delay and threshold have a cumulative effect on the total health detection." Its example: a CrossSubnetDelay of 2 seconds with a CrossSubnetThreshold of 10 "means that the cluster can have a total network tolerance of 20 seconds before recovery action is taken." With the newer same-subnet defaults, the Azure article puts it as "if connectivity monitoring fails for 10 seconds, the failover Threshold is reached resulting in the unreachable that node being removed from cluster membership."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Thresholds at the defaults for your version</td>
<td>Normal; the network lost heartbeats for longer than the default window, so find out why (Steps 6 to 8)</td>
</tr>
<tr>
<td>SameSubnetThreshold 5 on a cluster that should have 10</td>
<td>An older default on a Windows Server 2012 R2 cluster; Microsoft's tuning article recommends the higher values for those versions</td>
</tr>
<tr>
<td>Thresholds already high (for example 60 or 120)</td>
<td>Someone already masked it once, and it still fails; the network problem is bigger than the last fix</td>
</tr>
<tr>
<td>RouteHistoryLength 10 with raised thresholds</td>
<td>Microsoft recommends RouteHistoryLength "to be twice the value" of the threshold, so the cluster log keeps enough history</td>
</tr>
<tr>
<td>A stretched cluster across sites or subnets</td>
<td>Cross-subnet or cross-site values apply; Microsoft's only real case for raising them is "a multi-site scenario where nodes reside in different locations and network latency can't be overcome."</td>
</tr>
<tr>
<td>Nodes in Azure IaaS</td>
<td>Microsoft's Azure article recommends relaxed values there; read Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: What does the cluster log say? (read-only)</h2>
<p>The System log tells you a node was removed. The cluster log tells you which route went first and what the heartbeat counters looked like. Generate it for a window around the 1135, in local time:</p>
<pre><code class="language-powershell"># Read-only: from an empty working folder, a cluster log from every node for the last 120 minutes, local timestamps, copied here
Get-ClusterLog -TimeSpan 120 -UseLocalTime -Destination .
</code></pre>
<p>Microsoft's reference for the two switches that matter here: <code>-TimeSpan</code> "Specifies the time span in minutes for which to generate the cluster log." and <code>-UseLocalTime</code> "Specifies that the time stamp for each cluster log entry uses local time. By default, the timestamp uses Greenwich Mean Time (GMT)." If the 1135 was more than a couple of hours ago, raise <code>-TimeSpan</code> to cover it; without <code>-Destination</code>, Microsoft says the log is created "in the cluster reports folder C:\Windows\Cluster\Reports on each node of the cluster." With <code>-Destination .</code>, Microsoft's example "creates a log file for each node of the local cluster, and copies all logs to the local folder."</p>
<p>Then search the logs for the route lines Microsoft's Azure thresholds article shows in its example:</p>
<pre><code class="language-powershell"># Read-only: route-down, unreachable, route history, and quorum lines in every node's cluster log
Select-String -Path .\*.log -Pattern 'Marking Route', 'unreachable', 'Route history', 'Lost quorum' |
    Select-Object Filename, LineNumber, Line
</code></pre>
<p>Microsoft's example log shows the sequence: "Signaled NetftRemoteUnreachable event", then "Marking Route from" one address "to" another "as down", then "All routes for route (virtual) local" and "are down", then "Route history" lines with "Heartbeats counter/threshold: 5/5", and finally, in that example, "Lost quorum". What to read (our reading):</p>
<table>
<thead>
<tr>
<th>What you find</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td><code>Marking Route</code> lines for one network only, and the node stayed in</td>
<td>That network dropped heartbeats; the other one held. Read Step 7 for that network's adapters</td>
</tr>
<tr>
<td>Routes on every network marked down within the same second</td>
<td>Something shared went: a host, team, virtual switch, upstream switch, or a policy refresh that blocked all traffic (Steps 7 and 8)</td>
</tr>
<tr>
<td>"Route history" lines with counter/threshold at the limit, then the removal</td>
<td>The threshold was reached; compare with the time of the backup, migration, or policy refresh</td>
</tr>
<tr>
<td>The remote address in the unreachable line is the same node every time</td>
<td>That node's side (Scenario A)</td>
</tr>
<tr>
<td>"Lost quorum"</td>
<td>The survivors couldn't keep a majority; read Step 3's quorum branch</td>
</tr>
<tr>
<td>No route lines at all, but the node's service stopped</td>
<td>Not a heartbeat loss; read that node's System and Application logs</td>
</tr>
<tr>
<td>Nothing near the time you expected</td>
<td>The cluster log is GMT unless you used <code>-UseLocalTime</code>, or <code>-TimeSpan</code> didn't reach back far enough</td>
</tr>
</tbody></table>
<p>The route history depth is RouteHistoryLength from Step 5. If you plan to raise thresholds, raise that too, or the next log won't show enough of the heartbeats before the drop.</p>
<hr />
<h2>Step 7: What did the NIC, driver, team, and switch see? (read-only)</h2>
<p>Microsoft's heartbeat article puts hardware first among the causes: "Actual network hardware failures. If the packet is lost on the wire somewhere between the nodes, then the heartbeats fail." Read the adapters on the node that was removed and on one that wasn't:</p>
<pre><code class="language-powershell"># Read-only: adapters with Microsoft's Driver view (driver provider, version, and date)
Get-NetAdapter -Name * | Format-Table -View Driver

# Read-only: all statistics for one adapter, including discards and errors
Get-NetAdapterStatistics -Name 'Cluster1' | Format-List -Property "*"

# Read-only: Microsoft's discarded-packets counter, 12 samples 5 seconds apart, during the busy window
Get-Counter -Counter '\Network Interface(*)\Packets Received Discarded' -SampleInterval 5 -MaxSamples 12
</code></pre>
<p>Microsoft's guidance on that counter: "if they are any value higher than zero, then the receive buffer needs to be adjusted up for the adapter." Its heartbeat article adds how to read it: "This counter is cumulative and only increases until the server is rebooted. Seeing a large number of packets dropped here could be a sign that the receive buffers on the network card are set too low or that the server is performing slowly and can't handle the inbound traffic." So take two readings across the backup or migration window and compare them, rather than reading one number once (our reading).</p>
<p>Then read the team and virtual switch, if there is one, since a heartbeat route on a team or a virtual switch depends on both:</p>
<pre><code class="language-powershell"># Read-only: LBFO teams and their state
Get-NetLbfoTeam

# Read-only: Hyper-V virtual switches, and Switch Embedded Teaming members if used
Get-VMSwitch
Get-VMSwitchTeam -Name 'SETswitch'
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Old driver or firmware dates on the removed node, or different versions across nodes</td>
<td>Microsoft: "Old or out-of-date network card drivers and/or firmware. At times, a simple misconfiguration of the network card or switch can also cause loss of heartbeats."</td>
</tr>
<tr>
<td>Discarded packets climbing during the window</td>
<td>Receive buffers too small or the host too busy; Microsoft says to use the manufacturer's recommended values</td>
</tr>
<tr>
<td>The heartbeat network is a team, and a member flapped at the time of the 1135</td>
<td>Microsoft: "If you have NIC teaming enabled on the heartbeat NIC, check Teaming software functionality as per teaming vendor's recommendation."</td>
</tr>
<tr>
<td>All cluster networks share one team or one virtual switch</td>
<td>One failure takes every route; that matches routes marked down in the same second (Step 6)</td>
</tr>
<tr>
<td>Guest cluster on VMware with VMXNET3 adapters</td>
<td>Microsoft's VMware article: "VMXNET3 adapters dropping inbound network packets because the inbound buffer is set too low to handle large amounts of traffic."</td>
</tr>
<tr>
<td>Guest cluster on Hyper-V or VMware with old integration components or tools</td>
<td>Microsoft lists "outdated NIC drivers\Integration Components (IC)\VmTools or faulty NIC adapters"</td>
</tr>
<tr>
<td>Discards zero, drivers current, but latency high between sites</td>
<td>Not packet loss; heartbeats arriving late. Microsoft: "The packets might not be lost between the nodes, but they might not get to the nodes fast enough before the timeout period expires."</td>
</tr>
</tbody></table>
<p>For the VMware guest case, Microsoft's 1135 article gives the adapter settings it changes, in the guest's Device Manager under the vmxnet3 adapter's <strong>Advanced</strong> tab: "Select Small Rx Buffers and increase the value. The default value is 512 and the maximum is 8192." and "Select Rx Ring #1 Size and increase the value. The default value is 1024 and the maximum is 4096." Read the current values first:</p>
<pre><code class="language-powershell"># Read-only: the vmxnet3 adapter's advanced properties (look for the Rx buffer and ring settings)
Get-NetAdapterAdvancedProperty -Name 'Ethernet0'
</code></pre>
<hr />
<h2>Step 8: Did a firewall, IPsec, profile, or antivirus change block it? (read-only)</h2>
<p>These are the causes that make a 1135 look random, because nothing is broken afterwards. Read each one for the minute of the event.</p>
<p><strong>Firewall and ports.</strong> Microsoft's 1135 article lists what the cluster needs:</p>
<table>
<thead>
<tr>
<th>Application</th>
<th>Protocol</th>
<th>Port (Microsoft's table)</th>
</tr>
</thead>
<tbody><tr>
<td>Cluster Service</td>
<td>UDP</td>
<td>3343</td>
</tr>
<tr>
<td>Cluster Service</td>
<td>TCP</td>
<td>3343 (This port is required during a node join operation.)</td>
</tr>
<tr>
<td>RPC</td>
<td>TCP</td>
<td>135</td>
</tr>
<tr>
<td>Cluster Admin</td>
<td>UDP</td>
<td>137</td>
</tr>
<tr>
<td>Kerberos</td>
<td>UDP/TCP</td>
<td>464</td>
</tr>
<tr>
<td>SMB</td>
<td>TCP</td>
<td>445</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only: Microsoft's check for the cluster heartbeat rule
netsh advfirewall firewall show rule name="Failover Clusters (UDP-In)" verbose

# Read-only: every rule in the Failover Clusters group, and whether it's enabled
Get-NetFirewallRule -DisplayGroup 'Failover Clusters' | Format-Table -Property DisplayName, Enabled, Direction, Action -AutoSize

# Read-only: the IPv6 Neighbor Discovery rules Microsoft's heartbeat article names
Get-NetFirewallRule -DisplayName 'Core Networking - Neighbor Discovery*' | Format-Table -Property DisplayName, Enabled, Direction -AutoSize
</code></pre>
<p>Microsoft ties IPv6 to the heartbeat directly: "IPv6 is the default protocol that Failover Clustering will use for its heartbeats." Its list of causes includes IPv6 enabled with "Core Networking - Neighbor Discovery Advertisement" and "Core Networking - Neighbor Discovery Solicitation" disabled, and "If there are switches, firewalls, or routers not configured properly to allow this traffic through, you can experience issues like this." Event 1572 on a joining node says the same in its own words: "Also verify the Windows Firewall 'Failover Clusters' rules." Microsoft also says the Cluster Network Driver adapter is placed for you: "In Windows Server 2012 and Windows Server 2012 R2, the Cluster Network Driver (NETFT.SYS) adapter is automatically placed at the bottom in the binding order list."</p>
<p><strong>Network profile.</strong> Microsoft: "The profile for your network connections could possibly be bouncing from Domain to Public and back to Domain again. During the transition of these changes, network I/O can be blocked." The log is <strong>Applications and Services Logs\Microsoft\Windows\NetworkProfile\Operational</strong>:</p>
<pre><code class="language-powershell"># Read-only: the current profile of each adapter
Get-NetConnectionProfile | Format-Table -Property InterfaceAlias, Name, NetworkCategory -AutoSize

# Read-only: find the exact name of the NetworkProfile Operational log on this node
$np = (Get-WinEvent -ListLog '*NetworkProfile*Operational').LogName

# Read-only: its events around the 1135 (set the window to your event time)
Get-WinEvent -FilterHashtable @{
    LogName   = $np
    StartTime = (Get-Date '2026-10-13 01:00')
    EndTime   = (Get-Date '2026-10-13 01:15')
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Id, Message
</code></pre>
<p><strong>IPsec policy refresh.</strong> Microsoft: "during an IPSec group policy update all IPsec Security Associations (SAs) are torn down by Windows Firewall with Advanced Security (WFAS). While this is happening, all network connectivity is blocked." If renegotiation waits on Active Directory, the block can outlast the threshold. A 1135 that lines up with the Group Policy refresh interval on nodes with connection security rules is this case until proven otherwise (our reading); Event 1592's text names the same thing: "communication temporarily being blocked by a firewall or connection security policy update."</p>
<p><strong>Antivirus.</strong> Microsoft: "Anti-virus software could be interfering with this process also." Its 1135 article gives the exclusions it expects, and they're worth reading against your antivirus policy:</p>
<table>
<thead>
<tr>
<th>Microsoft's exclusion</th>
<th>Applies to</th>
</tr>
</thead>
<tbody><tr>
<td>"The path of the FileShare Witness"</td>
<td>Clusters with a file share witness</td>
</tr>
<tr>
<td>"The %Systemroot%\Cluster folder"</td>
<td>Every node</td>
</tr>
<tr>
<td>"Default virtual machine configuration directory (C:\ProgramData\Microsoft\Windows\Hyper-V)"</td>
<td>Hyper-V hosts</td>
</tr>
<tr>
<td>"Default virtual hard disk drive directory (C:\Users\Public\Documents\Hyper-V\Virtual Hard Disks)"</td>
<td>Hyper-V hosts, plus any custom VM, VHD, replica, and snapshot directories</td>
</tr>
<tr>
<td>Vmwp.exe, as a process exclusion</td>
<td>Hyper-V hosts</td>
</tr>
<tr>
<td>"exclude the CSV path C:\Clusterstorage and all its subdirectories"</td>
<td>Clusters with Cluster Shared Volumes and live migration</td>
</tr>
</tbody></table>
<p>Microsoft's caution about testing it: "Just disabling the antivirus software is insufficient in most cases. Even if you disable the antivirus software, the filter driver is still loaded when you restart the computer."</p>
<p><strong>Validation.</strong> Microsoft's 1135 article puts this early: "A validation and the network tests would be recommended as one of the initial troubleshooting steps to ensure there are no configuration issues that might be a cause for problems." Run only the network tests on a live cluster, and list the test names on your version first:</p>
<pre><code class="language-powershell"># Read-only: list the validation tests and categories on this version
Test-Cluster -List

# Read-only for networking: run the network communication and firewall tests only, with a named report
Test-Cluster -Include 'Validate Network Communication', 'Validate Windows Firewall Configuration' -ReportName C:\Temp\cluster-network-validation
</code></pre>
<p>Microsoft's descriptions of those two tests: "This test validates that tested servers can communicate with acceptable latency on all networks. The test also checks to see if there are redundant communication paths between all servers." and "This test validates that Windows Firewall is configured correctly for failover clustering on the tested servers." In the report, Microsoft's example latency failure reads "the ping latency is greater than the maximum allowed 2000 ms". Keep storage tests out of a run on a live cluster: Microsoft's <code>-Disk</code> and <code>-Force</code> notes describe disks taken offline for the duration of the storage tests.</p>
<hr />
<h2>Step 9: Change one link (a change)</h2>
<p>Fix only what Steps 2 to 8 showed, then read the result before changing anything else. If the change touches a node, drain it first:</p>
<pre><code class="language-powershell"># A change: move this node's roles off gracefully before you touch its NICs, drivers, or switch ports
Suspend-ClusterNode -Name HV02 -Drain -Wait

# After the change and a read of Steps 4 and 7 on it: bring it back, with its roles moved back now
Resume-ClusterNode -Name HV02 -Failback Immediate
</code></pre>
<p>Microsoft's <code>-Drain</code>: "Specifies that all of the workloads are gracefully moved to other nodes while maintaining the highest levels of availability and using the best placement logic." <code>-Failback</code> takes NoFailBack, Immediate, or Policy.</p>
<p><strong>Drivers, firmware, and buffers (Step 7).</strong> Update the NIC driver and firmware to the versions your server and NIC vendors recommend, on one drained node at a time, and raise receive buffers to the manufacturer's recommended values. For a VMware guest cluster, Microsoft's direction is to "gradually increase the number of buffers in the guest operating system and verify." Re-read the discard counter under the same load afterwards.</p>
<p><strong>Network roles and metrics (Step 4).</strong> Give the cluster a second path for heartbeats, and keep storage traffic off cluster communication. Microsoft's Role values: 1 for internal cluster communication, 3 for cluster and client, 0 for not used by the cluster. Microsoft's restriction: "If this network is the only one used for internal cluster communication, the Role property may not be changed to ClusterNetworkRoleNone or ClusterNetworkRoleClientAccess."</p>
<pre><code class="language-powershell"># A change: let a second network carry cluster communication (Role 1); use your own network name
(Get-ClusterNetwork -Name 'Cluster Network 2').Role = 1

# A change, Microsoft's form: rank a network by metric (lower is preferred for cluster traffic)
$n = Get-ClusterNetwork 'Cluster Network 3'
$n.Metric = 1050
</code></pre>
<p>Microsoft's network prioritization article shows how to undo a manual metric: set <code>AutoMetric</code> back to <code>$true</code> on that network.</p>
<p><strong>Firewall, IPv6, profile, IPsec (Step 8).</strong> Re-enable the Failover Clusters rules and the Neighbor Discovery rules in the policy that disabled them, not by hand on one node (our reading). Fix the reason a profile drops to Public (usually domain detection at boot or a DNS path; our reading). For IPsec, Microsoft's article points at the refresh and the authentication delay; exempt cluster traffic in the connection security rules or fix the DC path the renegotiation waits on (our reading).</p>
<p><strong>Antivirus (Step 8).</strong> Add Microsoft's exclusions from the table above in the antivirus policy for every node, then reboot one drained node at a time so filter drivers load with the new policy.</p>
<p><strong>Quarantine (Step 3).</strong> Once the cause is fixed, clear the quarantine with Microsoft's command instead of waiting out the two hours:</p>
<pre><code class="language-powershell"># A change: start the Cluster service on the node and clear its quarantine
Start-ClusterNode -Name HV02 -ClearQuarantine
</code></pre>
<p><strong>Thresholds, last and in moderation (Step 5).</strong> Raise them only for the case Microsoft describes (stretched clusters, Azure IaaS, or older versions still on 5), and only after the network read. Microsoft: "In general, continuing to send frequent heartbeats but having greater thresholds is the preferred method." Its forms:</p>
<pre><code class="language-powershell"># A change, Microsoft's form: raise the same-subnet threshold (example value from Microsoft's article)
(Get-Cluster).SameSubnetThreshold = 20

# A change, Microsoft's form: keep twice as much route history as the threshold
(Get-Cluster).RouteHistoryLength = 40

# Re-read
Get-Cluster | Format-List -Property SameSubnet*, CrossSubnet*, RouteHistoryLength
</code></pre>
<p>Microsoft's tuning article: "These setting can be set while the cluster is up and running with no downtime and will take effect immediately with no need to reboot or restart the cluster." Its limit: "it is generally not recommended to exceed the TCP reconnect timeouts. Generally, this means not going above ~20 seconds." So a 1000 ms delay with a threshold of 20 is about the ceiling for most workloads, and its warning still applies: "increasing resiliency to network hiccups comes at the cost of increased downtime when a hard failure occurs." (The <code>RouteHistoryLength</code> value of 40 is the reference maximum; 40 is twice 20.)</p>
<hr />
<h2>Step 10: Prove it stays quiet (read-only, through the same load window)</h2>
<p>Wait through the window that produced the 1135 (the backup, the migration wave, the patch night, the Group Policy refresh), then run the reads again:</p>
<pre><code class="language-powershell"># Read-only: no new 1135, 1127, 1129, 1130, 1177, or 1672 on any node since the change
$since = Get-Date '2026-10-20 18:00'
foreach ($n in (Get-ClusterNode).Name) {
    Get-WinEvent -ComputerName $n -FilterHashtable @{ LogName = 'System'; Id = 1127, 1129, 1130, 1135, 1177, 1672; StartTime = $since } -ErrorAction SilentlyContinue |
        Select-Object @{n='LoggedOn';e={$n}}, TimeCreated, Id
}

# Read-only: nodes, networks, and interfaces all Up
Get-ClusterNode
Get-ClusterNetwork | Format-Table -Property Name, State, Role, Metric -AutoSize
Get-ClusterNetworkInterface | Sort-Object Network, Node | Format-Table -AutoSize

# Read-only: the cluster log for the window, with no route marked down
Get-ClusterLog -TimeSpan 120 -UseLocalTime -Destination .
Select-String -Path .\*.log -Pattern 'Marking Route' | Select-Object Filename, LineNumber, Line

# Read-only: discards across the same window, compared with the reading before the change
Get-Counter -Counter '\Network Interface(*)\Packets Received Discarded' -SampleInterval 5 -MaxSamples 12

# Read-only: the network validation tests again
Test-Cluster -Include 'Validate Network Communication', 'Validate Windows Firewall Configuration' -ReportName C:\Temp\cluster-network-validation-after
</code></pre>
<p>Pass criteria (our reading): no 1135 on any node through at least one full cycle of the window that caused it (a week, for a weekly backup); no route marked down in the cluster log for that window; every node, network, and interface Up; discarded packets flat across the busy window; at least two networks allowed for cluster communication; the network validation tests pass without latency or firewall warnings; no node quarantined; and heartbeat values at the defaults for your version, or raised in moderation with RouteHistoryLength at twice the threshold and a written reason.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it doesn't help (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Raising SameSubnetThreshold and CrossSubnetThreshold to the maximum</td>
<td>Microsoft: "it just masks the problem. It doesn't fix anything." Hard failures then take minutes to detect</td>
</tr>
<tr>
<td>Raising the delay instead of the threshold</td>
<td>Microsoft: "continuing to send frequent heartbeats but having greater thresholds is the preferred method."</td>
</tr>
<tr>
<td>Reading the 1135 on one node only</td>
<td>Microsoft's scenarios A, B, and C are told apart only by comparing all nodes</td>
</tr>
<tr>
<td>Comparing the System log with a cluster log in GMT</td>
<td><code>Get-ClusterLog</code> timestamps are GMT unless you use <code>-UseLocalTime</code>, so the route lines appear hours away</td>
</tr>
<tr>
<td>Evicting and re-adding the node</td>
<td>The NIC, driver, switch, or policy that dropped the heartbeats comes back with it</td>
</tr>
<tr>
<td>Disabling antivirus to test</td>
<td>Microsoft: "Just disabling the antivirus software is insufficient in most cases." The filter driver stays loaded</td>
</tr>
<tr>
<td>Disabling IPv6 on the cluster networks</td>
<td>Microsoft says IPv6 is the default heartbeat protocol; its cause is the Neighbor Discovery rules being disabled, not IPv6 itself</td>
</tr>
<tr>
<td>Running a full <code>Test-Cluster</code> on a live cluster</td>
<td>Storage tests can take disks offline; run the network tests by name</td>
</tr>
<tr>
<td>Clearing quarantine before the cause is fixed</td>
<td>The node leaves again; three times in an hour quarantines it again</td>
</tr>
<tr>
<td>Using one team or one virtual switch for every cluster network</td>
<td>Every route then fails together, and a node with two networks still gets removed</td>
</tr>
<tr>
<td>Treating 1069 or 1146 as the cause</td>
<td>They're resource and health events that follow the removal; read the 1135 first</td>
</tr>
</tbody></table>
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Discards climbing on one node's adapter</td>
<td>Driver, firmware, and receive buffers to vendor-recommended values, one drained node at a time</td>
<td>Raising thresholds so the drops stop mattering</td>
</tr>
<tr>
<td>VMware guest cluster with VMXNET3 drops</td>
<td>Microsoft's Small Rx Buffers and Rx Ring #1 Size changes, increased gradually</td>
<td>Moving the guest cluster to a single vNIC</td>
</tr>
<tr>
<td>Only one network carries cluster communication</td>
<td>A second network with Role 1 or 3, or a team on redundant switches</td>
<td>Setting the iSCSI network to carry cluster traffic to get a second path</td>
</tr>
<tr>
<td>Live migration or backup traffic on the heartbeat network</td>
<td>A dedicated live migration network and metrics that keep cluster traffic on its own path</td>
<td>Turning off live migration or backups</td>
</tr>
<tr>
<td>Neighbor Discovery or Failover Clusters rules disabled by policy</td>
<td>Re-enable them in the policy that disabled them</td>
<td>A local rule on one node that the next policy refresh removes</td>
</tr>
<tr>
<td>Profile dropping to Public</td>
<td>Fix domain detection on that adapter</td>
<td>Disabling the firewall on cluster networks</td>
</tr>
<tr>
<td>IPsec refresh blocking traffic</td>
<td>Exempt cluster traffic in the connection security rules, or fix the DC path</td>
<td>Lengthening the threshold to outlast every refresh</td>
</tr>
<tr>
<td>Stretched cluster or Azure IaaS with real latency</td>
<td>Cross-subnet or cross-site thresholds raised in moderation, with RouteHistoryLength at twice the threshold</td>
<td>Same-subnet values raised to 120 for a site link problem</td>
</tr>
<tr>
<td>A quarantined node, cause fixed</td>
<td><code>Start-ClusterNode -ClearQuarantine</code></td>
<td>Waiting out two hours, or evicting the node</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does Event 1135 "Cluster node was removed from the active failover cluster membership" mean?</strong>
The other nodes stopped receiving the node's heartbeats on every cluster network for longer than the threshold allows, or its Cluster service stopped. Microsoft's text: "The Cluster service on this node may have stopped. This could also be due to the node having lost communication with other active nodes in the failover cluster." It's logged on the nodes that remain, not on the removed one (Steps 1 and 2).</p>
<p><strong>What port does the failover cluster heartbeat use?</strong>
UDP 3343. Microsoft: "The heartbeat itself is a UDP unicast network packet that communicates over Port 3343." TCP 3343 is used when a node joins, and IPv6 is the default heartbeat protocol, so the Neighbor Discovery rules matter too (Step 8).</p>
<p><strong>What are the default SameSubnetDelay and SameSubnetThreshold values?</strong>
It depends on the version and the page you read. Microsoft's property reference lists SameSubnetDelay 1000 ms and SameSubnetThreshold 5; its tuning article lists 10 heartbeats for Windows Server 2016, and its Azure article says Windows Server sets SameSubnetThreshold to 10 by default. Read your own with <code>Get-Cluster | Format-List *subnet*</code> (Step 5).</p>
<p><strong>Should I increase the cluster heartbeat thresholds to stop Event 1135?</strong>
Only after the network read, and in moderation. Microsoft says raising them "simply masks the problem by making health monitoring less sensitive", and that it's generally "not going above ~20 seconds". The real cases are stretched clusters, Azure IaaS, and older versions still on 5 (Steps 5 and 9).</p>
<p><strong>Why is my cluster node quarantined, and how do I clear it?</strong>
On Windows Server 2016 and later, "a node is quarantined, if it ungracefully leaves the cluster, three times within an hour", for 7200 seconds by default. Fix the cause of the 1135s, then run <code>Start-ClusterNode -ClearQuarantine</code>, the command Microsoft's quarantine event names (Steps 3 and 9).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/hyper-v-live-migration-failed-event-21502-a-read-first-switch-processor-version-and-kerberos-181f">Hyper-V Live Migration Failed (Event 21502)</a> (when the 1135s line up with live migrations on the same hosts and networks)</li>
<li><a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed</a> (when the 1135s line up with the backup window and checkpoints fail too)</li>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (when the same nodes also fail RPC to domain controllers through the same firewall or NIC)</li>
</ul>
<h2>Free checklist</h2>
<p>Raising heartbeat thresholds on every cluster, or evicting the node, before anyone read which route dropped and why, is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=failover-cluster-event-1135">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=failover-cluster-event-1135</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=failover-cluster-event-1135">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=failover-cluster-event-1135</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 and 10 are read-only checks: <code>Get-WinEvent</code>, <code>Get-ClusterNode</code>, <code>Get-ClusterResource</code>, <code>Get-ClusterQuorum</code>, <code>Get-Cluster</code>, <code>Get-ClusterNetwork</code>, <code>Get-ClusterNetworkInterface</code>, <code>Get-ClusterLog</code>, <code>Select-String</code>, <code>Get-NetAdapter</code>, <code>Get-NetAdapterStatistics</code>, <code>Get-NetAdapterAdvancedProperty</code>, <code>Get-Counter</code>, <code>Get-NetLbfoTeam</code>, <code>Get-VMSwitch</code>, <code>Get-VMSwitchTeam</code>, <code>Get-NetFirewallRule</code>, <code>netsh advfirewall firewall show rule</code>, <code>Get-NetConnectionProfile</code>, <code>Get-Service</code> through <code>Invoke-Command</code>, and <code>Test-Cluster</code> with only the network tests (it writes a report file). <code>Get-ClusterLog</code> writes log files to the destination folder. Draining and resuming nodes, updating drivers and firmware, changing adapter buffers, network roles, and metrics, changing firewall, IPsec, and antivirus policy, clearing quarantine, and changing heartbeat thresholds or route history are deliberate changes. Change one link at a time, prove it through the same load window, and check current Microsoft Learn for version-specific behavior.</em></p>
]]></content:encoded></item><item><title><![CDATA[DNS Event ID 4013 (DNS Server Waiting for AD DS Initial Sync): A Read-First DNS Client, Replication, and 4015 Triage]]></title><description><![CDATA[DNS Event 4013, waiting for AD DS initial sync? Read how long it lasts, the DC's DNS servers, and its replication partners before the registry fix.]]></description><link>https://adminpackstudio.hashnode.dev/dns-event-id-4013-dns-server-waiting-for-ad-ds-initial-sync-a-read-first-dns-client-replication-and-4015-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/dns-event-id-4013-dns-server-waiting-for-ad-ds-initial-sync-a-read-first-dns-client-replication-and-4015-triage</guid><category><![CDATA[sysadmin]]></category><category><![CDATA[windows server]]></category><category><![CDATA[dns]]></category><category><![CDATA[Active Directory]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 14:47:53 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "After Sunday's patch reboot, DC01 sat at 'Preparing network connections' for about twenty minutes. Now DNS Manager on it says the server could not be contacted, and the DNS Server log has Event 4013 every two minutes: 'The DNS server is waiting for Active Directory Domain Services (AD DS) to signal that the initial synchronization of the directory has been completed.' DC02 was patched and rebooted at the same time. A forum post says to set Repl Perform Initial Synchronizations to 0 and point DC01 at 127.0.0.1 only. Can we do that?"</p>
<p>Not that way. <strong>Event 4013 usually isn't a broken DNS server. A DNS server that hosts AD-integrated zones waits after boot until AD DS finishes its first inbound replication, and AD DS can't replicate until it can resolve its partners' names through DNS. When the DC points only at itself, when its partners are rebooting too, offline, or long gone, or when the network isn't up yet, the two wait on each other.</strong> Microsoft says it plainly: "DNS Event ID 4013 in the DNS event log indicates that DNS service startup was delayed. It's because inbound replication of Active Directory partitions hadn't occurred." It's just as plain about the registry shortcut: "This configuration change isn't recommended for use in production environments, or in any environment on an ongoing basis." Pointing a DC with partners at itself only is the other half of the forum advice, and Microsoft's 4013 article lists that exact setup among the conditions that make 4013 worse. Work through it as a chain: <strong>which DC, which events, since when</strong> → <strong>is it a boot-time wait that ends, or one that never ends?</strong> → <strong>what are the DNS Server and AD DS services actually doing?</strong> → <strong>which DNS servers does this DC ask, and do they answer?</strong> → <strong>can it resolve its replication partners?</strong> → <strong>is inbound replication happening, and from whom?</strong> → <strong>is it the only DC, a role holder, or a recovery case?</strong> → <strong>is it really 4015, 4000, or 4007?</strong> → <strong>change one link</strong> → <strong>prove a clean boot.</strong> <code>Get-WinEvent</code>, <code>Get-Service</code>, <code>Get-DnsClientServerAddress</code>, <code>Resolve-DnsName</code>, <code>Test-NetConnection</code>, <code>repadmin /showrepl</code>, <code>repadmin /replsummary</code>, <code>Get-ADReplicationPartnerMetadata</code>, <code>Get-ADReplicationFailure</code>, <code>dcdiag /test:dns</code>, and <code>Get-ItemProperty</code> are the sources for that chain. Set the registry value "to be safe" and you may bring up a DC that serves stale zones and acts on stale role data, and the next reboot of two DCs at once still hangs the same way.</p>
<p>If the partners show replication error 1722 once you can read them, <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> covers the DNS, RPC, and firewall side. If member servers log Netlogon 5719 in the same boot window, <a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> covers the client view of the same timing problem. This post covers <strong>"a domain controller that runs DNS with AD-integrated zones and logs DNS Server Event 4013 (the DNS server is waiting for AD DS initial synchronization), with or without 4015, 4000, or 4007, a slow 'Preparing network connections' boot, DNS Manager or Active Directory Users and Computers failing to connect, and you need a read-first path before you change the registry, point the DC at itself, or restart services in a loop."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><p><strong>A short 4013 run after boot can be normal.</strong> Microsoft: "The delay won't occur if Active Directory has completed its initial synchronization during Windows startup." The event "will be logged every two minutes until AD DS has signaled that the initial synchronization has successfully completed", so count the events and read how long they lasted (Step 2).</p>
</li>
<li><p><strong>DNS waits for AD DS, and AD DS needs DNS to find its partners.</strong> Replication "is delayed until it can resolve the CNAME GUID of its source domain controller to an IP address on the DNS servers used by the destination domain controller for name resolution." (Steps 4 and 5).</p>
</li>
<li><p><strong>A DC with partners shouldn't point only at itself.</strong> Microsoft: "Domain controllers hosting AD-integrated DNS zones shouldn't point to a single domain controller and especially only to themselves as preferred DNS for name resolution." A forest with one DC is the exception (Steps 4 and 7).</p>
</li>
<li><p><strong>Stale or offline partners keep the wait going.</strong> Read <code>repadmin /showrepl</code> and remove the metadata of DCs that are really gone; don't wait on them (Step 6).</p>
</li>
<li><p><strong>The registry value is a temporary emergency tool.</strong> Repl Perform Initial Synchronizations set to 0 "should be used only in critical situations to resolve temporary and specific problems. The default setting should be restored after such problems are resolved." (Steps 7 and 9).</p>
</li>
<li><p><strong>4015 is a different problem.</strong> It's "a critical error from the Active Directory" with an error code that picks the cause: RODC, permissions, or too many orphaned records (Step 8).</p>
</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which DC, which events with their full text, when it booted, which other DCs rebooted with it, and what changed</td>
<td>"DNS is down on DC01" with no event text and no boot time</td>
</tr>
<tr>
<td>2. Event read</td>
<td>You know whether 4013 stopped after a few minutes or is still being logged, and which neighbor events came with it</td>
<td>Every 4013 treated as an outage; or a 4013 that's been logged for hours treated as boot noise</td>
</tr>
<tr>
<td>3. Services</td>
<td>DNS Server, AD DS, Netlogon, and KDC states are known, and you know whether the DC is advertising</td>
<td>DNS Server shows Starting or Running with no zones; DNS Manager can't connect</td>
</tr>
<tr>
<td>4. DC DNS client</td>
<td>The DC's adapter lists DNS servers that are up at boot, host the AD zones, and answer</td>
<td>Only 127.0.0.1 or its own address on a DC with partners; an ISP resolver; a retired DC</td>
</tr>
<tr>
<td>5. Partner names</td>
<td>The DC can resolve each source DC's GUID CNAME and host record from the servers it asks</td>
<td>NTDS 2087 or 2088; <code>Resolve-DnsName</code> fails for the <code>_msdcs</code> CNAME</td>
</tr>
<tr>
<td>6. Inbound replication</td>
<td>Every partner in <code>repadmin /showrepl</code> is online and replicates inbound</td>
<td>Partners that no longer exist; partners powered off; RPC errors such as 1722</td>
</tr>
<tr>
<td>7. Special cases</td>
<td>Single DC, role holder, isolated network, or forest recovery is recognized for what it is</td>
<td>Repl Perform Initial Synchronizations left at 0 from an old fix; a lone DC that waits on nothing</td>
</tr>
<tr>
<td>8. 4015, 4000, 4007</td>
<td>The event is identified by ID and error code, not lumped in with 4013</td>
<td>4015 with 0000051B, 00002095, or ADMIN_LIMIT_EXCEEDED treated as a boot wait</td>
</tr>
<tr>
<td>9. Change</td>
<td>One link fixed, then replication and the DNS Server service checked</td>
<td>Registry, DNS list, and services all changed in one go</td>
</tr>
<tr>
<td>10. Proof</td>
<td>A reboot in a window logs no lasting 4013, replication is clean, and every DNS server answers</td>
<td>It works until the next patch night, when two DCs reboot together again</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which DC, and is it a DNS server with AD-integrated zones?</strong></td>
<td>4013 is logged by DNS servers that store zones in AD DS, so it only appears on DCs running the DNS Server role (Step 3)</td>
</tr>
<tr>
<td><strong>The full event text and data</strong></td>
<td>4013's wording differs between Windows versions and Microsoft pages; 4015 carries an error code that decides the branch (Steps 2 and 8)</td>
</tr>
<tr>
<td><strong>When the DC booted, and how long the 4013s lasted</strong></td>
<td>A few events right after boot that then stop is a different case from 4013 every two minutes for an hour (Step 2)</td>
</tr>
<tr>
<td><strong>Which other DCs rebooted at the same time</strong></td>
<td>Microsoft's example is two DCs pointing at each other that both restart; each waits for the other's DNS (Step 4)</td>
</tr>
<tr>
<td><strong>How many DCs, sites, and domains</strong></td>
<td>A single DC, a branch DC behind a WAN link, and a hub DC with local partners need different DNS client settings (Steps 4 and 7)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>Patching, a power or cooling outage, a DC removed without demotion, a restore or recovery, a new IP address, DNS client changes on the DC, a registry fix someone applied once, an RODC added</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>A few 4013s right after boot, then they stop</td>
<td>Initial synchronization finished; the DNS Server service started; nothing to fix unless the boot was slow</td>
<td>Step 2</td>
</tr>
<tr>
<td>4013 every two minutes for an hour or more</td>
<td>AD DS hasn't finished inbound replication of its partitions; find out why</td>
<td>Steps 4 to 6</td>
</tr>
<tr>
<td>Two DCs that point at each other rebooted together</td>
<td>Microsoft's example scenario: each waits on the other's DNS while its own DNS waits on AD DS</td>
<td>Steps 4 and 9</td>
</tr>
<tr>
<td>A DC with partners that points only at itself</td>
<td>Microsoft lists this among the conditions that make 4013 worse</td>
<td>Step 4</td>
</tr>
<tr>
<td>One DC in a site, alternate DNS across a WAN link that was down</td>
<td>Microsoft's single-DC-in-a-site scenario</td>
<td>Steps 4 and 6</td>
</tr>
<tr>
<td><code>repadmin /showrepl</code> shows a partner that no longer exists</td>
<td>AD DS keeps trying to replicate from it; remove its metadata</td>
<td>Step 6</td>
</tr>
<tr>
<td>NTDS Event 2087 or 2088 in the Directory Service log</td>
<td>The DC can't resolve a partner's GUID CNAME; 2087 means replication from it failed</td>
<td>Step 5</td>
</tr>
<tr>
<td>The only DC in the forest logs 4013</td>
<td>There's no partner to replicate from; read its DNS client list and the registry value</td>
<td>Step 7</td>
</tr>
<tr>
<td>Repl Perform Initial Synchronizations is 0</td>
<td>Someone used the emergency bypass and never restored it</td>
<td>Steps 7 and 9</td>
</tr>
<tr>
<td>4015 "has encountered a critical error from the Active Directory"</td>
<td>A different event; the error code in the text picks the cause</td>
<td>Step 8</td>
</tr>
<tr>
<td>4000 or 4007, zones not loading, Access Denied in DNS Manager</td>
<td>Microsoft's 4000/4007 article ties this to the DC's secure channel</td>
<td>Step 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the events and how long they lasted (read-only)</h2>
<p>4013 is in the <strong>DNS Server</strong> log (Event Viewer: <strong>Applications and Services Logs &gt; DNS Server</strong>), not the System log. Read it with its neighbors, and read the Directory Service log for the same window:</p>
<pre><code class="language-powershell"># Read-only: DNS Server events since the last boot, oldest first
$boot = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
Get-WinEvent -FilterHashtable @{
    LogName   = 'DNS Server'
    Id        = 4000, 4007, 4011, 4013, 4015
    StartTime = $boot
} -ErrorAction SilentlyContinue |
    Sort-Object TimeCreated |
    Select-Object TimeCreated, Id, LevelDisplayName, Message

# Read-only: first and last 4013 since boot, and how many
$w = Get-WinEvent -FilterHashtable @{ LogName = 'DNS Server'; Id = 4013; StartTime = $boot } -ErrorAction SilentlyContinue
if ($w) {
    $first = ($w | Sort-Object TimeCreated | Select-Object -First 1).TimeCreated
    $last  = ($w | Sort-Object TimeCreated | Select-Object -Last 1).TimeCreated
    [pscustomobject]@{ Boot = $boot; Count = @($w).Count; First = $first; Last = $last; Minutes = [int]($last - $first).TotalMinutes }
}

# Read-only: DNS lookup failures for replication partners in the same window
Get-WinEvent -FilterHashtable @{
    LogName      = 'Directory Service'
    ProviderName = 'Microsoft-Windows-ActiveDirectory_DomainService'
    Id           = 2087, 2088
    StartTime    = $boot
} -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id, Message
</code></pre>
<p>Read the result against Microsoft's text for each event:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Log or source (as Microsoft shows it)</th>
<th>Microsoft's message (excerpt)</th>
<th>What it tells you (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>4013</td>
<td>DNS Server</td>
<td>"The DNS server is waiting for Active Directory Domain Services (AD DS) to signal that the initial synchronization of the directory has been completed."</td>
<td>DNS is waiting on AD DS initial sync; the rest of this post</td>
</tr>
<tr>
<td>4013 (older wording)</td>
<td>DNS</td>
<td>"The DNS server was unable to open the Active Directory. This DNS server is configured to use directory service information and can not operate without access to the directory."</td>
<td>The same event ID as Microsoft's 4013 troubleshooting article shows it</td>
</tr>
<tr>
<td>4015</td>
<td>DNS Server</td>
<td>"The DNS server has encountered a critical error from the Active Directory. Check that the Active Directory is functioning properly."</td>
<td>Not a boot wait; read the extended error (Step 8)</td>
</tr>
<tr>
<td>4000</td>
<td>DNS Server</td>
<td>"The DNS server was unable to open Active Directory. This DNS server is configured to obtain and use information from the directory for this zone and is unable to load the zone without it."</td>
<td>Zones not loading (Step 8)</td>
</tr>
<tr>
<td>4007</td>
<td>DNS Server</td>
<td>"The DNS server was unable to open zone" (then the zone and application directory partition)</td>
<td>One zone's partition can't be opened (Step 8)</td>
</tr>
<tr>
<td>4011</td>
<td>DNS</td>
<td>"The DNS server was unable to add or write an update of domain name"</td>
<td>An AD write failed; Microsoft's large-record article shows it alongside 4015</td>
</tr>
<tr>
<td>2087</td>
<td>Directory Service</td>
<td>"Active Directory could not resolve the following DNS host name of the source domain controller to an IP address."</td>
<td>Replication from that source failed on DNS (Step 5)</td>
</tr>
<tr>
<td>2088</td>
<td>Directory Service</td>
<td>"Active Directory could not use DNS to resolve the IP address of the source domain controller listed below."</td>
<td>Replication worked through fallback, but DNS is still wrong (Step 5)</td>
</tr>
</tbody></table>
<p>Two details matter when you compare with forum posts. First, the waiting text is the one on Microsoft's event reference page (symbolic name DNS_EVENT_DS_OPEN_WAIT), and it ends: "This event will be logged every two minutes until AD DS has signaled that the initial synchronization has successfully completed." Microsoft's current troubleshooting article for 4013 shows the older "unable to open the Active Directory" wording instead. Both are 4013 from the DNS server; search for the event ID, not only the text. Second, the waiting text gives a direction of its own: "If events in the AD DS event log indicate that there is a problem with DNS name resolution, consider adding the IP address of another DNS server for this domain to the DNS server list in the Internet Protocol properties of this computer."</p>
<p>Microsoft's 4013 article lists event data values it can carry, and notes they "may not be logged":</p>
<table>
<thead>
<tr>
<th>Hex</th>
<th>Decimal</th>
<th>Symbolic name</th>
<th>Microsoft's text</th>
</tr>
</thead>
<tbody><tr>
<td>000025f5</td>
<td>9717</td>
<td>DNS_ERROR_DS_UNAVAILABLE</td>
<td>The directory service is unavailable</td>
</tr>
<tr>
<td>0000232d</td>
<td>9005</td>
<td>DNS_ERROR_RCODE_REFUSED</td>
<td>DNS operation refused.</td>
</tr>
<tr>
<td>0000232a</td>
<td>9002</td>
<td>DNS_ERROR_RCODE_SERVER_FAILURE</td>
<td>DNS server failure.</td>
</tr>
</tbody></table>
<p>Then read the timing (our reading):</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>A handful of 4013s, the last within a few minutes of boot, then DNS answering</td>
<td>Initial sync finished; note the boot length and move on unless it was the 15 to 25 minute hang</td>
</tr>
<tr>
<td>4013s for 15 to 20 minutes, then they stop</td>
<td>Microsoft's described delay: "these domain controllers can experience a 15-20 minute startup delay." Read Steps 4 to 6 so the next boot is faster</td>
</tr>
<tr>
<td>4013 still being logged now</td>
<td>AD DS hasn't finished; find the partner or the DNS path that's missing (Steps 4 to 6)</td>
</tr>
<tr>
<td>4013 plus 2087 for a partner</td>
<td>That partner's name doesn't resolve from this DC (Step 5)</td>
</tr>
<tr>
<td>4013 on the only DC</td>
<td>No partner exists; read Step 7</td>
</tr>
<tr>
<td>4015 or 4000/4007 without 4013</td>
<td>Go to Step 8</td>
</tr>
</tbody></table>
<p>Microsoft's own test explains why the wait can be that long: when the DNS Server service isn't available, "NTDS will need to send out 10 DNS queries to resolve the IP address", and with the default DNSQueryTimeouts "DNS client will wait 12 (1 + 1 + 2 + 4 + 4) seconds for the DNS server response." Multiply by the partitions (schema, configuration, domain, and the two DNS application partitions) and the minutes add up.</p>
<hr />
<h2>Step 3: What are the services doing? (read-only)</h2>
<p>Read the services the chain depends on, and whether the DC is advertising yet. DNS Server, AD DS, Netlogon, and KDC are the ones that matter here:</p>
<pre><code class="language-powershell"># Read-only: the services behind DNS and AD DS on this DC
Get-Service -Name DNS, NTDS, Netlogon, Kdc |
    Select-Object Name, DisplayName, Status, StartType

# Read-only: which DC does DC locator return from here? (this DC itself once it advertises; our reading)
nltest /dsgetdc:contoso.com
</code></pre>
<p>Then try the consoles once, without restarting anything. Microsoft's example scenarios show what a DC stuck in this state looks like:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>Microsoft's text</th>
<th>What it means (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>DNS Manager (DNSMGMT.MSC) won't connect</td>
<td>"The server <code>&lt;computername&gt;</code> could not be contacted. The error was: The server is unavailable. Would you like to add it anyway?"</td>
<td>The DNS Server service isn't serving yet; it's waiting on AD DS</td>
</tr>
<tr>
<td>Active Directory Users and Computers (DSA.MSC) won't open</td>
<td>"Naming information could not be located"</td>
<td>The DC can't yet act as a DC for the console</td>
</tr>
<tr>
<td>A long boot</td>
<td>"This issue occurs after the Preparing network connections message is displayed, and before the Windows logon prompt (Ctrl+Alt+Del) is displayed."</td>
<td>The wait happened during startup; read Step 2's timing</td>
</tr>
<tr>
<td>DNS Server Running, zones present, 4013s stopped</td>
<td>(none)</td>
<td>Initial sync finished; the problem, if any, is how long it took</td>
</tr>
<tr>
<td>DNS Server Running, 4000/4007, Access Denied in DNS Manager</td>
<td>Microsoft's 4000/4007 symptoms</td>
<td>Step 8, not a boot wait</td>
</tr>
</tbody></table>
<p>Restarting the DNS Server service while AD DS is still synchronizing doesn't shorten the wait (our reading): Microsoft's resolution for the event says "Ensure that AD DS is functioning properly, troubleshoot any problems, and then restart the DNS Server service." Fix the AD DS side first.</p>
<hr />
<h2>Step 4: Which DNS servers does this DC ask? (read-only)</h2>
<p>This is the step that most often explains a long 4013. Read every adapter's DNS server list, in order:</p>
<pre><code class="language-powershell"># Read-only: DNS servers per adapter, in the order the DC uses them
Get-DnsClientServerAddress -AddressFamily IPv4 |
    Where-Object { $_.ServerAddresses } |
    Select-Object InterfaceAlias, InterfaceIndex, ServerAddresses

# Read-only: the same from ipconfig, with suffix and registration details
ipconfig /all
</code></pre>
<p>Read the list against Microsoft's guidance for DCs:</p>
<table>
<thead>
<tr>
<th>What the list shows</th>
<th>Microsoft's guidance (quoted) or our reading</th>
</tr>
</thead>
<tbody><tr>
<td>Only 127.0.0.1 or the DC's own address, on a DC with partners</td>
<td>"Configuring domain controllers to point to a single DNS server's IP address, including the 127.0.0.1 loopback address, represents a single point of failure. This setting is tolerable in a forest with only one domain controller, but not in forests with multiple domain controllers."</td>
</tr>
<tr>
<td>Two DCs that each list the other first and themselves second, both rebooted together</td>
<td>Microsoft's own example: "When both DC1 and DC2 reboot simultaneously, there will be a slow startup because of this mutual dependency." The fix is the reboot schedule and a third DNS server that's up, not removing the partner (Step 9)</td>
</tr>
<tr>
<td>A hub-site DC</td>
<td>"Hub-site domain controllers should point to DNS servers in the same site as them for preferred and alternate DNS server and then finally to itself as another alternate DNS server."</td>
</tr>
<tr>
<td>A branch-site DC</td>
<td>"Branch site domain controllers should configure the preferred DNS server IP address to point to a hub-site DNS server, the alternate DNS server IP address to point to an in-site DNS server or one in the closest available site, and finally to itself using the 127.0.0.1 loopback address or current static IP address."</td>
</tr>
<tr>
<td>An ISP or public resolver</td>
<td>"Don't configure the DNS client settings on the domain controllers to point to DNS servers of your Internet Service Provider (ISP)." Use forwarders instead (<a href="https://dev.to/adminpackstudio/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-57fl">Windows DNS Server Not Resolving External Names</a> covers that side)</td>
</tr>
<tr>
<td>A retired DC's address, or a server that doesn't host the zones</td>
<td>Microsoft lists DNS servers "that either don't exist, are currently offline, aren't accessible on the network, or that don't host the required zones and records that are needed to inbound-replicate Active Directory" among the 4013 conditions</td>
</tr>
<tr>
<td>The first and only DC in the domain</td>
<td>"If the server is the first and only domain controller that you install in the domain, and the server runs DNS, configure the DNS client settings to point to that first server's IP address." (Step 7)</td>
</tr>
</tbody></table>
<p>Microsoft's summary of what a DC's DNS servers must do is short: they should be servers that "Are available at Windows startup." and that "Host, forward, or delegate the <code>_msdcs.&lt;forest root domain&gt;</code> and primary DNS suffix zones for current and potential source domain controllers." Two client details from Microsoft's best-practice article explain why a good-looking list can still fail: the client uses the preferred server until it fails to respond, and "Only a failure to respond will cause the DNS client to switch Preferred DNS servers; receiving an authoritative but incorrect response does not cause the DNS client to try another server."</p>
<p>Then prove each listed server answers for the AD zones right now. Run this from the DC (or from another machine, against the same servers):</p>
<pre><code class="language-powershell"># Read-only: does each DNS server in the DC's list answer on port 53 and hold the zones?
$servers = '10.0.0.11', '10.1.0.10', '127.0.0.1'
foreach ($s in $servers) {
    $tcp = Test-NetConnection -ComputerName $s -Port 53 -WarningAction SilentlyContinue
    $soa = Resolve-DnsName -Name 'contoso.com' -Type SOA -Server $s -DnsOnly -ErrorAction SilentlyContinue
    $msd = Resolve-DnsName -Name '_msdcs.contoso.com' -Type SOA -Server $s -DnsOnly -ErrorAction SilentlyContinue
    [pscustomobject]@{ Server = $s; Tcp53 = $tcp.TcpTestSucceeded; DomainSOA = [bool]$soa; MsdcsSOA = [bool]$msd }
}
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>Every server in the list is a DC that rebooted with this one</td>
<td>Nothing could answer during the boot window; that's the 4013 loop</td>
</tr>
<tr>
<td>The only server that answers is 127.0.0.1</td>
<td>This DC's own DNS, which is the one waiting on AD DS; it can't help AD DS find partners</td>
</tr>
<tr>
<td>A server answers but has no <code>_msdcs</code> zone</td>
<td>It can't resolve the GUID CNAMEs replication uses (Step 5)</td>
</tr>
<tr>
<td>Port 53 fails to one server</td>
<td>It's off, retired, or blocked; it's still costing timeouts on every boot</td>
</tr>
<tr>
<td>A partner in another site answers</td>
<td>Good, as long as the WAN link is up at boot; Microsoft's single-DC-in-a-site scenario is the case where it wasn't</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Can this DC resolve its replication partners? (read-only)</h2>
<p>AD DS finds a source DC by the GUID-based alias in <code>_msdcs</code>. Microsoft: "You should ensure that domain controllers can successfully resolve the guided CNAME records to host records of current and potential source domain controllers." Read the partner's CNAME from replication data, then resolve it from each server in this DC's list:</p>
<pre><code class="language-powershell"># Read-only: inbound partners with their fully qualified CNAME (look for the _msdcs name in the output)
repadmin /showrepl DC01 /verbose

# Read-only: resolve one partner's GUID CNAME and host record from a specific DNS server
$guidName = 'dded5a29-fc25-4fd8-aa98-7f472fc6f09b._msdcs.contoso.com'
Resolve-DnsName -Name $guidName -Type CNAME -Server 10.0.0.11 -DnsOnly
Resolve-DnsName -Name 'DC02.contoso.com' -Type A -Server 10.0.0.11 -DnsOnly
</code></pre>
<p>Microsoft documents <code>/verbose</code> as showing the "fully qualified CNAME, invocation ID, replication flags, and update sequence number (USN) values" for each source partner, which is the name you need. If AD DS isn't answering on this DC yet, run <code>repadmin /showrepl</code> against a healthy DC instead and read its view of this DC.</p>
<p>Then run Microsoft's DNS tests from the 2087 procedure, against this DC and against the partner:</p>
<pre><code class="language-powershell"># Read-only: basic DNS health (connectivity, services, client config, records, zone and SOA)
dcdiag /test:dns /s:DC01 /DnsBasic /f:C:\Temp\dcdiag-dns-DC01.txt

# Read-only: are the A, CNAME, and SRV records for DC location registered?
dcdiag /test:dns /s:DC02 /DnsRecordRegistration
</code></pre>
<p>Microsoft's description of the basic test covers what you'd otherwise check by hand: it confirms the DNS Client, Net Logon, KDC, and DNS Server services are running, that "DNS servers on all adapters are reachable", and that each DC's host record "is registered on at least one of the DNS servers that is configured on the client." <code>/DnsRecordRegistration</code> checks "the address (A), canonical name (CNAME) and well-known service (SRV) resource records". Note that <code>/DnsDynamicUpdate</code> is not read-only: Microsoft says it performs "registration of a test record (_dcdiag_test_record)", deleted automatically after the test.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>CNAME missing on every server</td>
<td>The partner never registered it, or it was removed; Microsoft notes DNS scavenging "can be configured too aggressively, causing valid records to be prematurely purged from DNS zones." (<a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers how)</td>
</tr>
<tr>
<td>CNAME present on one server, missing on another</td>
<td>Replication or zone scope differs between them; the DC asks the one that doesn't have it</td>
</tr>
<tr>
<td>CNAME resolves, host record points to an old address</td>
<td>A stale A record; Microsoft: "Missing, duplicate, or stale CNAME and host records all contribute to this problem."</td>
</tr>
<tr>
<td>2087 for a partner</td>
<td>Microsoft: "attempts to resolve the globally unique identifier (GUID) in the alias (CNAME) resource record, the fully qualified domain name (FQDN), and the NetBIOS name to the IP address of the source domain controller have all failed."</td>
</tr>
<tr>
<td>2088 for a partner</td>
<td>Replication succeeded by FQDN or NetBIOS fallback; the CNAME is still broken, and every boot pays for the fallback</td>
</tr>
<tr>
<td>The partner is gone, but its CNAME is still asked for</td>
<td>It's still in this DC's replication data; Step 6</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Is inbound replication happening, and from whom? (read-only)</h2>
<p>Microsoft's cause statement is about partners: these DCs "try to inbound replicate all locally held directory partitions during Windows startup, as part of an initial synchronization or init sync." Read who they are and whether each one works:</p>
<pre><code class="language-powershell"># Read-only: inbound replication status for this DC, by partition and partner
repadmin /showrepl DC01

# Read-only: only the failing partnerships, for every DC in the forest
repadmin /showrepl * /errorsonly

# Read-only: failures and largest deltas, summarized per source and destination
repadmin /replsummary * /bysrc /bydest /sort:delta
</code></pre>
<p>The PowerShell view gives the same data as objects, once AD DS on the DC you query is answering:</p>
<pre><code class="language-powershell"># Read-only: inbound partners of DC01, last success and last result
Get-ADReplicationPartnerMetadata -Target DC01 -PartnerType Inbound |
    Select-Object Partner, Partition, LastReplicationAttempt, LastReplicationSuccess, LastReplicationResult

# Read-only: current replication failures recorded for DC01
Get-ADReplicationFailure -Target DC01 |
    Select-Object Partner, FailureCount, FirstFailureTime, LastError

# Read-only: every DC the domain knows about, with site and address
Get-ADDomainController -Filter * |
    Select-Object HostName, Site, IPv4Address, IsGlobalCatalog, OperationMasterRoles
</code></pre>
<p>Read the partners against Microsoft's list of conditions that keep initial sync from finishing:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Microsoft's condition (quoted)</th>
<th>Next step (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>A partner that was removed without demotion or rebuilt under the same name</td>
<td>"The local domain controller's copy of Active Directory contains references to stale domain controllers that no longer exist on the network."</td>
<td>Metadata cleanup for that DC (Step 9)</td>
</tr>
<tr>
<td>A partner that's powered off for now</td>
<td>"The local domain controller's copy of Active Directory contains references to other domain controllers who are currently turned off."</td>
<td>Bring it up, or wait it out with another partner available</td>
</tr>
<tr>
<td>Partners online but failing with an error</td>
<td>"So the local domain controller's copy of Active Directory contains references to other domain controllers that are online and accessible but can't be successfully replicated from."</td>
<td>Read the error; 1722 is covered in <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a></td>
</tr>
<tr>
<td>Every partner fails, and the network adapter shows no connectivity</td>
<td>"The network adapter or network stack on the caller or target computer is either disabled or non-functional."</td>
<td>Fix the adapter or virtual switch first</td>
</tr>
<tr>
<td>The DC was started on a lab or recovery network</td>
<td>"The domain controller has been booted on an isolated network."</td>
<td>Expected; see Step 7's recovery note</td>
</tr>
<tr>
<td>One partner succeeds, others fail</td>
<td>Initial sync needs each partition from a partner; a success for one partition doesn't cover the others (our reading)</td>
<td>Read the failures per partition</td>
</tr>
</tbody></table>
<p>Microsoft's guidance for the gone-for-good case is direct: "Remove references to stale domain controllers." and "Make offline or non-functioning domain controllers operational." Its 2087 article adds the check before cleanup: confirm the source DC is really down, with <code>net view \\&lt;SourceDomainControllerName&gt;</code> (which "displays the Netlogon and SYSVOL shares, indicating that the server is functioning as a domain controller.").</p>
<hr />
<h2>Step 7: Is it the only DC, a role holder, or a recovery case? (read-only)</h2>
<p>Three situations look like 4013 trouble but need their own reading.</p>
<p><strong>The only DC in the forest.</strong> Microsoft's best-practice article says the first and only DC that runs DNS should point at itself: "Do not list any other DNS servers until you have another domain controller hosting DNS in that domain." With no partner, there's nothing to replicate from, so a long 4013 on a lone DC usually means its copy of AD DS still lists a partner (our reading): a second DC that was removed without demotion, or a DC from a test you forgot. Run Step 6's <code>Get-ADDomainController -Filter *</code> and <code>repadmin /showrepl</code> on it; any partner listed there is one it's waiting on.</p>
<p><strong>A role holder.</strong> Operations masters have their own initial-sync rule, and Microsoft's 4013 article keeps the two apart: "The initial sync requirements required for FSMO roles to become operational is different from the initial sync discussed in this article, where Active Directory must inbound replicate to start the DNS Server service immediately." The role rules, from Microsoft's FSMO article:</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>Microsoft's initial replication requirement</th>
</tr>
</thead>
<tbody><tr>
<td>Schema master</td>
<td>"This FSMO role holder is only active when the role owner has inbound replicated the schema NC successfully since the Directory Service started."</td>
</tr>
<tr>
<td>Domain naming master</td>
<td>"This FSMO role holder is only active when the role owner has inbound replicated the configuration NC successfully since the Directory Service started."</td>
</tr>
<tr>
<td>RID master, infrastructure master</td>
<td>"This FSMO role holder is active only when the role owner has inbound replicated the domain NC successfully since the Directory Service started."</td>
</tr>
<tr>
<td>PDC emulator</td>
<td>"There is no inbound replication requirement."</td>
</tr>
</tbody></table>
<p>So a role holder that logs 4013 may also be refusing role operations, such as RID pool requests, for the same reason (our reading); fix the replication, not the roles.</p>
<p><strong>The registry value.</strong> Read it on every DC you touch, because an old fix often left it behind:</p>
<pre><code class="language-powershell"># Read-only: is the initial synchronization bypass set? (absent means the default)
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name 'Repl Perform Initial Synchronizations' -ErrorAction SilentlyContinue |
    Select-Object 'Repl Perform Initial Synchronizations'
</code></pre>
<p>Microsoft's 4013 article gives the bypass as the value Repl Perform Initial Synchronizations, type REG_DWORD, data 0, under <code>HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Parameters</code>, and then says "The use of Repl Perform Initial Synchronizations should be used only in critical situations to resolve temporary and specific problems." The forest recovery guide is where it's meant to be used: for a restored DC that holds operations master roles, so AD DS isn't unavailable until it has replicated a writeable partition. That guide's restore instruction is "After the forest is recovered completely, you can reset the value of this entry to 1, which requires a domain controller that restarts and holds operations master roles to have successful AD DS inbound and outbound replication with its known replica partners before it advertises itself as domain controller and starts providing services to clients."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Value absent</td>
<td>The default; nothing to change</td>
</tr>
<tr>
<td>Value 1</td>
<td>The default behavior, set explicitly (Microsoft's recovery guide resets it to 1)</td>
</tr>
<tr>
<td>Value 0 and no one knows why</td>
<td>A leftover bypass; plan to restore it (Step 9) after replication is proven</td>
</tr>
<tr>
<td>Value 0 during a documented forest recovery</td>
<td>Expected, until the forest is recovered</td>
</tr>
<tr>
<td>DC started on a lab, recovery, or isolated network</td>
<td>Microsoft lists "The domain controller has been booted on an isolated network." among the 4013 conditions; 4013 there is expected</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: Is it really 4015, 4000, or 4007? (read-only)</h2>
<p>These come from the same DNS Server and AD DS relationship but aren't boot waits. Read the full text of the most recent ones:</p>
<pre><code class="language-powershell"># Read-only: the newest 4015, 4000, and 4007 with their full message, including the extended error
Get-WinEvent -FilterHashtable @{ LogName = 'DNS Server'; Id = 4015, 4000, 4007 } -MaxEvents 10 -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id, Message |
    Format-List
</code></pre>
<p>Microsoft's 4015 text is "The DNS server has encountered a critical error from the Active Directory. Check that the Active Directory is functioning properly. The extended error debug information (which may be empty) is", followed by the error. Its 4015 article sorts the error into three cases:</p>
<table>
<thead>
<tr>
<th>Extended error starts with</th>
<th>Microsoft's cause</th>
<th>Microsoft's check or fix</th>
</tr>
</thead>
<tbody><tr>
<td>00002095 (often with "problem 5012 (DIR_ERROR)"), logged every three minutes on an RODC</td>
<td>"A Read-Only Domain Controller (RODC) running DNS can't connect to a writable domain controller"</td>
<td>Run <code>nltest /dsgetdc:</code> with the domain and <code>/WRITABLE /AVOIDSELF /TRY_NEXT_CLOSEST_SITE /DS_6</code>; the DC it returns must be writeable, run DNS, and have correct records</td>
</tr>
<tr>
<td>0000051B (with "nTSecurityDescriptor")</td>
<td>"The SYSTEM account isn't the owner of the DNS zone." or "The Users group for the domain doesn't have the correct members, including the Authenticated Users group."</td>
<td>Zone owner set to SYSTEM; the domain Users group contains Domain Users, Authenticated Users, and INTERACTIVE</td>
</tr>
<tr>
<td>ADMIN_LIMIT_EXCEEDED (problem 5008)</td>
<td>"The DNS Server service generates this error code if a multi-valued dnsRecord attribute of a dnsNode object in Active Directory contains too many values."</td>
<td>Remove the orphaned records of demoted DCs, by record data</td>
</tr>
</tbody></table>
<p>The read-only checks for each branch:</p>
<pre><code class="language-powershell"># Read-only (00002095, on the RODC): which writable DC does DC locator return?
nltest /dsgetdc:contoso.com /WRITABLE /AVOIDSELF /TRY_NEXT_CLOSEST_SITE /DS_6

# Read-only (0000051B): members of the domain's Users group
net localgroup users /domain

# Read-only (0000051B): owner of each AD-integrated zone object in the domain DNS partition
Get-ADObject -SearchBase 'CN=MicrosoftDNS,DC=DomainDnsZones,DC=contoso,DC=com' -LDAPFilter '(objectClass=dnsZone)' -Properties nTSecurityDescriptor |
    Select-Object Name, @{n='Owner';e={$_.nTSecurityDescriptor.Owner}}

# Read-only (ADMIN_LIMIT_EXCEEDED): record counts per name in the forest DNS partition, to a file
repadmin /showattr . "CN=MicrosoftDNS,DC=ForestDnsZones,DC=contoso,DC=com" /subtree /filter:"(objectclass=dnsnode)" /atts:"dnsRecord" /allvalues &gt; C:\Temp\dns_ForestDnsZones.txt
</code></pre>
<p>Microsoft's own route to the zone in the 0000051B case is Field Engineering diagnostic logging and Directory Service Event 1644 at the time of the 4015, then ADSI Edit: "Right-click the zone object, and then select Properties &gt; Security &gt; Advanced." and "Make sure that Owner is set to SYSTEM." Field Engineering logging is a registry change; turn it off afterwards, as Microsoft's steps do. For the record-count case, Microsoft explains why the values pile up: "However, when you demote a DC that acts as a DNS server, Active Directory doesn't automatically clean up the related DNS objects and attributes." Its large-zone article puts the ceiling at "approximately 1,200 values that can be associated with a single object in Windows Server 2022 and earlier versions."</p>
<p><strong>4000 and 4007</strong> mean zones didn't load, and Microsoft's article for that pair names a different cause: "This issue happens when that particular DC/DNS server has lost its Secure channel with itself or PDC." It adds: "This issue can also happen in a single DC environment where that DC/DNS server holds all the FSMO roles and is pointing to itself as Primary DNS server." Microsoft's symptoms include that "You notice that the DNS Server service is up and running." while DNS Manager gives Access Denied, which is how you tell it from a 4013 wait (our reading). Microsoft's fix for that case is in Step 9.</p>
<hr />
<h2>Step 9: Change one link, then let initial sync finish (a change)</h2>
<p>Fix only what Steps 2 to 8 showed, then read the result before changing anything else.</p>
<p><strong>The DC's DNS server list (Step 4).</strong> For a hub-site DC with partners, point it at in-site partners first and itself last, as Microsoft describes. Use the addresses from your own design:</p>
<pre><code class="language-powershell"># A change: partner DNS servers first, this DC's own loopback last
Set-DnsClientServerAddress -InterfaceAlias 'Ethernet' -ServerAddresses '10.0.0.11', '10.0.0.12', '127.0.0.1'

# Re-read it, then clear the resolver cache and re-register, as Microsoft's article says to after any change
Get-DnsClientServerAddress -InterfaceAlias 'Ethernet' -AddressFamily IPv4
ipconfig /flushdns
ipconfig /registerdns
</code></pre>
<p>Microsoft's rule for that last step: "If you change any DNS client settings, you must clear the DNS resolver cache and register the DNS resource records." For a branch DC, the order is hub first, then in-site, then itself (Step 4's table). For a lone DC, its own address is correct; don't add a server that doesn't host its zones.</p>
<p><strong>The reboot plan (Step 4).</strong> If the 4013 came from DCs that point at each other and rebooted together, the fix is the schedule as much as the list. Microsoft: "Stagger the reboots of DNS servers in your enterprise when possible." and, among its single points of failure, "Placing all DNS servers on guest virtual machines on the same physical host computer". Put DCs in different patch waves, and keep at least one DNS server that each DC lists up while the others restart.</p>
<p><strong>A partner that's gone for good (Step 6).</strong> Confirm it's down, then clean up its metadata. Microsoft's simplest path: deleting the DC's computer account from the Domain Controllers OU in Active Directory Users and Computers, where "the cleanup of server metadata is performed automatically." Then remove the DNS records it left behind, by record data, the way Microsoft's 4015 article does:</p>
<pre><code class="language-powershell"># A change: preview, then remove one gone DC's NS record from a zone (Microsoft's form, with -WhatIf added)
Remove-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'NS' -Name '@' -RecordData 'dc9.contoso.com' -WhatIf
Remove-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'NS' -Name '@' -RecordData 'dc9.contoso.com'
</code></pre>
<p><strong>A partner whose records are missing (Step 5).</strong> On that partner, re-register. Microsoft: "The Net Logon service on a domain controller registers the DNS resource records that are required for the domain controller to be located on the network. The DNS Client service registers the host (A) resource record that the alias (CNAME) record points to."</p>
<pre><code class="language-powershell"># A change, on the partner DC: re-register its locator records and its host record
Restart-Service -Name Netlogon
ipconfig /registerdns
</code></pre>
<p>Microsoft's next instruction is "Wait 15 minutes, and then review events in Event Viewer to ensure proper registration of the resource records." Then re-run Step 5's <code>Resolve-DnsName</code> from the DC that logged 4013.</p>
<p><strong>Pull replication once names resolve.</strong> Then restart DNS only after AD DS has synchronized:</p>
<pre><code class="language-powershell"># A change: synchronize DC01 from all its partners, all partitions, across sites, DNs in messages
repadmin /syncall DC01 /A /e /d

# When repadmin /showrepl DC01 is clean, restart the DNS Server service
Restart-Service -Name DNS
</code></pre>
<p><strong>A known bad boot (Steps 4 and 6).</strong> When you must boot a DC into a setup you know will stall (its partners are down for a planned outage, for example), Microsoft's documented path is to stop DNS from holding up the boot: "Set the DNS Server service startup value to manual.", then "Reboot, wait for the domain controller to advertise.", then "Restart the DNS Server service." It explains why that works: "If the service startup value for DNS Server service is set to manual, Active Directory doesn't wait for the DNS Server service to start."</p>
<pre><code class="language-powershell"># A change, before the planned boot: DNS Server service to manual
Set-Service -Name DNS -StartupType Manual

# After the boot, once nltest /dsgetdc returns this DC: start DNS, then put the startup type back
Start-Service -Name DNS
Set-Service -Name DNS -StartupType Automatic
</code></pre>
<p><strong>The registry value (Step 7).</strong> If it's 0 from an old fix, restore the default once replication is proven, as Microsoft's recovery guide does:</p>
<pre><code class="language-powershell"># A change: restore initial synchronization (Microsoft's recovery guide resets the value to 1)
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name 'Repl Perform Initial Synchronizations' -Value 1
</code></pre>
<p>Set it to 0 only in the situation Microsoft describes, a critical and temporary one, with a written plan to restore it, and expect the next reboot to be the real test.</p>
<p><strong>4015 with 0000051B (Step 8).</strong> Fix the zone owner in ADSI Edit as Microsoft describes, or restore the Users group membership with Microsoft's command, then wait for replication and restart the DNS Server service on all the DCs in the domain, as Microsoft's steps say:</p>
<pre><code class="language-console">net localgroup users "NT AUTHORITY\Authenticated Users" /add /domain
</code></pre>
<p><strong>4000 and 4007 (Step 8).</strong> Microsoft's resolution starts with the DNS list ("configure the server experiencing the issue to point to other active DNS server in TCP/IP properties"), then: "Stop the KDC service on the DC experiencing the issue.", reset the DC's machine account password against the PDC with <code>netdom resetpwd</code>, and "Once the command executes, reboot the server." Microsoft's command, with your PDC and admin account in place of its placeholders:</p>
<pre><code class="language-console">netdom resetpwd /server:DC02.contoso.com /userd:CONTOSO\admin1 /passwordd:*
</code></pre>
<p>That's a secure-channel repair, not a fix for 4013 (our reading); use it only when Step 8 showed 4000 or 4007 with the zones not loading.</p>
<hr />
<h2>Step 10: Prove a clean boot (read-only, after a planned reboot)</h2>
<p>Reboot the DC in a window, alone, with its partners up, and then run the reads again:</p>
<pre><code class="language-powershell"># Read-only: DNS Server events since this boot; Microsoft's verify step wants none from 4000 to 4019
$boot = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
Get-WinEvent -FilterHashtable @{ LogName = 'DNS Server'; Id = 4000..4019; StartTime = $boot } -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id, Message

# Read-only: the events Microsoft's verify step expects to see
Get-WinEvent -FilterHashtable @{ LogName = 'DNS Server'; Id = 4523, 4524; StartTime = $boot } -ErrorAction SilentlyContinue |
    Select-Object TimeCreated, Id

# Read-only: replication and DNS health after the boot
repadmin /showrepl DC01 /errorsonly
dcdiag /test:dns /s:DC01 /DnsBasic

# Read-only: every DNS server returns the DC locator records and the zones load here
foreach ($s in '10.0.0.10', '10.0.0.11', '10.0.0.12') {
    Resolve-DnsName -Name '_ldap._tcp.dc._msdcs.contoso.com' -Type SRV -Server $s -DnsOnly -ErrorAction SilentlyContinue |
        Select-Object @{n='Server';e={$s}}, Name, NameTarget, Port
}
Get-DnsServerZone -ComputerName DC01 | Select-Object ZoneName, ZoneType, IsDsIntegrated
</code></pre>
<p>Microsoft's verify step for the event is: "Ensure that Event IDs 4523 and 4524 are being logged and that no events in the range 4000 to 4019 appear in the Domain Name System (DNS) event log."</p>
<p>Pass criteria (our reading): no 4013 after the first few minutes of boot (none at all is better), and none in the 4000 to 4019 range after that; the boot no longer hangs at "Preparing network connections"; <code>repadmin /showrepl</code> shows every inbound partner succeeding; <code>dcdiag /test:dns /DnsBasic</code> passes for the DC; every DNS server returns the SRV records; DNS Manager opens and lists the AD-integrated zones; Repl Perform Initial Synchronizations is absent or 1; and the next patch night has DCs rebooting in separate waves.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it doesn't help (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Setting Repl Perform Initial Synchronizations to 0 and leaving it</td>
<td>Microsoft: "The default setting should be restored after such problems are resolved." Left at 0, the DC skips the check that keeps it from serving stale data</td>
</tr>
<tr>
<td>Pointing a DC with partners at 127.0.0.1 only</td>
<td>Microsoft names it a single point of failure, "tolerable in a forest with only one domain controller", and it's the setup where AD DS can't find partners while its own DNS waits</td>
</tr>
<tr>
<td>Adding a public resolver as a second DNS server</td>
<td>It can't answer for <code>_msdcs</code> or your domain; Microsoft says not to point DCs at ISP DNS servers</td>
</tr>
<tr>
<td>Restarting the DNS Server service in a loop</td>
<td>DNS waits on AD DS; until initial sync finishes or fails over, a restart starts the same wait</td>
</tr>
<tr>
<td>Rebooting the DC again "to be safe"</td>
<td>The same DNS list and the same partners give the same wait; fix Steps 4 to 6 first</td>
</tr>
<tr>
<td>Running <code>dcdiag /fix</code> or <code>/DnsDynamicUpdate</code> as a first read</td>
<td>Both change things (<code>/DnsDynamicUpdate</code> registers a test record); start with <code>/DnsBasic</code> and <code>/DnsRecordRegistration</code></td>
</tr>
<tr>
<td>Treating 4015 as a boot wait</td>
<td>4015 carries an error code that names the cause: RODC, permissions, or record limits (Step 8)</td>
</tr>
<tr>
<td>Running <code>netdom resetpwd</code> for every 4013</td>
<td>Microsoft's resetpwd steps are for 4000 and 4007 with a lost secure channel</td>
</tr>
<tr>
<td>Seizing roles because the role holder logs 4013</td>
<td>Role holders wait for their own partition to replicate; seizing doesn't make DNS start, and the old holder still has the roles when it comes back</td>
</tr>
<tr>
<td>Deleting a partner's computer account because it's offline today</td>
<td>Metadata cleanup is for DCs that are gone for good; Microsoft's 2087 article checks the source DC first</td>
</tr>
<tr>
<td>Adding HOSTS file entries for partners</td>
<td>Microsoft notes static entries "become invalid over time", and queries for one DC "may be incorrectly resolved to another domain controller"</td>
</tr>
</tbody></table>
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>A DC with partners points only at itself</td>
<td>In-site partners first and itself last (hub), or hub first, in-site next, itself last (branch)</td>
<td>127.0.0.1 as the only entry, or a public resolver as the alternate</td>
</tr>
<tr>
<td>Two DCs point at each other and rebooted together</td>
<td>Separate patch waves, plus a DNS server each DC lists that stays up</td>
<td>Removing the partner from the list so each DC points only at itself</td>
</tr>
<tr>
<td>A partner that's gone for good</td>
<td>Metadata cleanup for that DC, then its leftover DNS records removed by record data</td>
<td>Waiting it out, or setting the registry bypass so the DC stops asking</td>
</tr>
<tr>
<td>A partner that's offline for now</td>
<td>Bring it back, or make sure another partner and DNS server are up at boot</td>
<td>Metadata cleanup for a DC that's coming back</td>
</tr>
<tr>
<td>Partner CNAME or A record missing</td>
<td>Re-register on the partner (Netlogon restart and <code>ipconfig /registerdns</code>), then resolve again</td>
<td>HOSTS or LMHOSTS entries on the DC</td>
</tr>
<tr>
<td>Repl Perform Initial Synchronizations left at 0</td>
<td>Restore it to the default after replication is proven</td>
<td>Leaving it at 0 because the boot is faster</td>
</tr>
<tr>
<td>A planned boot into a known bad setup</td>
<td>Microsoft's manual DNS startup, then back to Automatic once the DC advertises</td>
<td>The registry bypass for a one-time maintenance boot</td>
</tr>
<tr>
<td>4015 with 0000051B</td>
<td>Zone owner back to SYSTEM, or the Users group membership restored</td>
<td>Granting broad rights on the DNS partitions to make the error stop</td>
</tr>
<tr>
<td>4000/4007 with Access Denied</td>
<td>Microsoft's secure-channel reset, after pointing the DC at another DNS server</td>
<td><code>netdom resetpwd</code> as a fix for a plain 4013 wait</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does Event 4013 "The DNS server is waiting for Active Directory Domain Services (AD DS) to signal that the initial synchronization of the directory has been completed" mean?</strong> The DNS Server service on a DC with AD-integrated zones is holding off until AD DS finishes its first inbound replication after boot, because "critical DNS data might not yet be replicated onto this domain controller." A few events right after boot that then stop are normal; events every two minutes for an hour mean replication isn't finishing (Steps 2 and 6).</p>
<p><strong>Why does a domain controller take 15 to 20 minutes to boot with Event 4013?</strong> AD DS needs DNS to find its replication partners, and DNS is waiting on AD DS. When the DC's DNS servers are itself or partners that are also rebooting, each lookup times out before falling back, for each partition. Microsoft's test puts it at "greater than 14 minutes" for five partitions and one source. Fix the DC's DNS server list and the reboot schedule (Steps 4 and 9).</p>
<p><strong>Should I set Repl Perform Initial Synchronizations to 0 to fix Event 4013?</strong> Not as a fix. Microsoft says it "isn't recommended for use in production environments, or in any environment on an ongoing basis" and should be used only in critical, temporary situations, with the default restored afterwards. Its forest recovery guide uses it for a restored role holder and then resets it to 1 (Steps 7 and 9).</p>
<p><strong>Should a domain controller point to itself for DNS?</strong> A lone DC should. A DC with partners shouldn't point only at itself: Microsoft says DCs "shouldn't point to a single domain controller and especially only to themselves as preferred DNS for name resolution." Put partners first and the DC itself last, following Microsoft's hub and branch guidance (Step 4).</p>
<p><strong>What's the difference between DNS Event 4013 and 4015?</strong> 4013 is a wait: DNS hasn't started serving because AD DS initial synchronization hasn't finished. 4015 is an error: "The DNS server has encountered a critical error from the Active Directory." Its extended error code picks the cause: 00002095 (an RODC can't reach a writable DC), 0000051B (zone owner or Users group permissions), or ADMIN_LIMIT_EXCEEDED (too many orphaned records) (Step 8).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><p><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (when a partner in repadmin /showrepl fails with the RPC server unavailable)</p>
</li>
<li><p><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (when member servers can't find a DC in the same boot window)</p>
</li>
<li><p><a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> (when a partner's CNAME or host record was aged out and deleted)</p>
</li>
<li><p><a href="https://dev.to/adminpackstudio/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-57fl">Windows DNS Server Not Resolving External Names</a> (when a DC lists a public resolver because the forwarders were wrong)</p>
</li>
</ul>
<h2>Free checklist</h2>
<p>Setting the initial synchronization bypass, or pointing every DC at itself, before anyone read which partner and which name lookup was failing, is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm%5C_source=hashnode&amp;utm%5C_medium=article&amp;utm%5C_campaign=aps%5C_first%5C_sale&amp;utm%5C_content=dns-event-4013-initial-sync">https://cashflow4375.gumroad.com/l/ujkqxb?utm\_source=hashnode&amp;utm\_medium=article&amp;utm\_campaign=aps\_first\_sale&amp;utm\_content=dns-event-4013-initial-sync</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm%5C_source=hashnode&amp;utm%5C_medium=article&amp;utm%5C_campaign=aps%5C_first%5C_sale&amp;utm%5C_content=dns-event-4013-initial-sync">https://cashflow4375.gumroad.com/l/joonf?utm\_source=hashnode&amp;utm\_medium=article&amp;utm\_campaign=aps\_first\_sale&amp;utm\_content=dns-event-4013-initial-sync</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 and 10 are read-only checks:</em> <code>Get-WinEvent</code><em>,</em> <code>Get-CimInstance</code><em>,</em> <code>Get-Service</code><em>,</em> <code>nltest /dsgetdc</code><em>,</em> <code>Get-DnsClientServerAddress</code><em>,</em> <code>ipconfig /all</code><em>,</em> <code>Test-NetConnection</code><em>,</em> <code>Resolve-DnsName</code><em>,</em> <code>repadmin /showrepl</code><em>,</em> <code>repadmin /replsummary</code><em>,</em> <code>repadmin /showattr</code><em>,</em> <code>dcdiag /test:dns /DnsBasic</code> <em>and</em> <code>/DnsRecordRegistration</code><em>,</em> <code>Get-ADReplicationPartnerMetadata</code><em>,</em> <code>Get-ADReplicationFailure</code><em>,</em> <code>Get-ADDomainController</code><em>,</em> <code>Get-ADObject</code><em>,</em> <code>net localgroup users /domain</code><em>,</em> <code>Get-ItemProperty</code><em>, and</em> <code>Get-DnsServerZone</code><em>. Changing a DC's DNS server list, re-registering records, cleaning up DC metadata, removing DNS records, forcing replication, restarting or changing the startup type of services, changing the Repl Perform Initial Synchronizations value, changing group membership or zone ownership, resetting a machine account password, and rebooting DCs are deliberate changes. Change one link at a time, prove it with a planned reboot, and check current Microsoft Learn for version-specific behavior.</em></p>
]]></content:encoded></item><item><title><![CDATA[Cluster Network Name Resource Failed Registration (Event 1196, 1257): A Read-First DNS, CNO, and Record Triage]]></title><description><![CDATA[Cluster network name failed registration, Event 1196 or 1257? Read the DNS error, each node's DNS servers, the zone, and who owns the record first.]]></description><link>https://adminpackstudio.hashnode.dev/cluster-network-name-resource-failed-registration-event-1196-1257-a-read-first-dns-cno-and-record-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/cluster-network-name-resource-failed-registration-event-1196-1257-a-read-first-dns-cno-and-record-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[dns]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 12:42:06 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since the storage maintenance on Saturday, the file server role on the Hyper-V cluster won't stay online on HVN02, and the System log there keeps showing Event 1196: 'Cluster network name resource 'Cluster Name' failed registration of one or more associated DNS name(s) for the following reason: DNS bad key.' There's also a 1257 for FS01. A forum post says to delete the A records, give Authenticated Users full control of the zone, set the zone to accept nonsecure updates, and run Repair. Can we do that?"</p>
<p>Not that way. <strong>"Cluster network name resource failed registration" usually isn't a broken cluster. The network name resource asks a DNS server to create or update its A (and optionally PTR) records, from the node that owns the role, with the cluster's identity; it fails when that node asks the wrong DNS server, when the zone won't take the update, when an existing record belongs to an identity the cluster can't act for, or when the computer object behind the name is disabled, missing, or out of sync. The event's reason text tells you which.</strong> Microsoft's own guidance for the secure-zone case is narrow: "Ensure that cluster name object (CNO) is granted permissions to the Secure DNS Zone." Its failover clustering blog adds the detail that explains most 1196 and 1257 events after a rebuild or a hand-made record: "By default, all Authenticated Users have permissions to create a new record inside a secure zone. But if a record already exists, the security principal (in this case the cluster name identity) should have Full Control over the existing DNS record." Opening the whole zone to nonsecure updates fixes the symptom by removing the security that keeps one machine from overwriting another's name. Work through it as a chain: <strong>which name, which event, which node</strong> → <strong>what does the event's reason or error code say?</strong> → <strong>what does the name resource itself report?</strong> → <strong>which DNS servers does the owner node ask, and can it reach them?</strong> → <strong>does the zone accept the update?</strong> → <strong>who owns the existing record?</strong> → <strong>is the computer object behind the name healthy?</strong> → <strong>is time or Kerberos in the way?</strong> → <strong>change one link</strong> → <strong>prove every DNS server answers with the right address.</strong> <code>Get-WinEvent</code>, <code>Get-ClusterResource</code>, <code>Get-ClusterParameter</code>, <code>Get-ClusterResourceDependency</code>, <code>Get-DnsClientServerAddress</code>, <code>Get-DnsClient</code>, <code>Resolve-DnsName</code>, <code>Get-DnsServerZone</code>, <code>Get-DnsServerResourceRecord</code>, <code>Get-ADComputer</code>, <code>Get-ADObject</code>, and <code>Get-ClusterLog</code> are the sources for that chain. Delete and recreate records "to be safe" and you may hand the name to the wrong owner, break every client that cached the old address, and still see the same event on the next failover.</p>
<p>If the record vanished because DNS scavenging deleted it, <a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers how aging and timestamps decide that. If a node lists a public resolver because someone wanted the internet to work, <a href="https://dev.to/adminpackstudio/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-57fl">Windows DNS Server Not Resolving External Names</a> covers the forwarder side, so the nodes can point only at your own DNS servers. This post covers <strong>"a failover cluster network name (the cluster name or a role's client access point) that won't register in DNS: Event 1196 with a reason such as 'DNS bad key' or 'DNS operation refused', Event 1195 with an error code, Event 1257 'access to update the secure DNS Zone was denied', 1256, 1258, 1259, 1260, 1578, 1579, or 1580, a name that resolves to an old address after a failover, or a role that won't come online on one node, and you need a read-first path before you delete records, loosen zone security, or run Repair."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Read the reason, not just the event ID.</strong> Event 1196 carries the DNS error as text ("for the following reason: %2"), and the name resource keeps the last result in its <code>StatusDNS</code> parameter, which Microsoft defines as "The returned status code from the last DNS operation that the NetName resource performed." (Steps 2 and 3).</li>
<li><strong>The owner node registers, through its own adapters.</strong> The cluster registers "over some physical adapter that can reach a DNS server", so one node with a public or retired DNS server in its list can fail where the others succeed (Step 4).</li>
<li><strong>The zone is the primary DNS suffix's zone.</strong> Microsoft: "The registration for a cluster network name always happens in the zone corresponding to the Primary DNS Suffix of the host." (Step 5).</li>
<li><strong>An existing record needs the cluster's identity on its ACL.</strong> A record created by hand, by DHCP, or by a cluster you've since rebuilt can block the update; read the record's owner before you touch it (Step 6).</li>
<li><strong>Repair fixes the computer object, not DNS permissions.</strong> Microsoft's Repair Active Directory Object is for the cluster name account's password mismatch, and it "will be greyed out unless the cluster's Name resource is offline." (Steps 7 and 9).</li>
<li><strong>Re-register without an outage.</strong> <code>Update-ClusterNetworkNameResource</code> "registers existing Network Name resources with a DNS server in a way that doesn't interrupt cluster availability." (Step 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which network name, which event IDs and reason text, which node owned the role at the time, and what changed</td>
<td>"The cluster name is broken" with no event text and no node</td>
</tr>
<tr>
<td>2. Event read</td>
<td>The reason or error code is known and mapped to a cause</td>
<td>Event 1196 noted, but nobody wrote down "DNS bad key" or "DNS operation refused"</td>
</tr>
<tr>
<td>3. Resource read</td>
<td>The name resource, its IP dependencies, its DnsName, and its last DNS status are known</td>
<td>The name is online with a warning; <code>StatusDNS</code> isn't 0; an IP address resource is offline</td>
</tr>
<tr>
<td>4. Node DNS client</td>
<td>Every node's cluster-facing adapters list only DNS servers that host your AD zones, and they answer</td>
<td>One node points at a public resolver or a retired DC; Event 1258 names an unreachable server</td>
</tr>
<tr>
<td>5. Zone</td>
<td>The zone that matches the primary DNS suffix exists, is AD-integrated, and accepts secure dynamic updates</td>
<td>Event 1256 or 1578; a zone set to None; the name registered in the wrong zone</td>
</tr>
<tr>
<td>6. Record owner</td>
<td>Either no record exists, or the existing record grants the cluster's identity rights to update it</td>
<td>Event 1257, 1259, or 1580 after a rebuild, a hand-made record, or a DHCP registration</td>
</tr>
<tr>
<td>7. Computer object</td>
<td>The CNO (and the role's VCO) exists, is enabled, is where the cluster expects it, and its password matches</td>
<td>"Logon failure: unknown user name or bad password."; Events 1193, 1194, 1206, 1207; a disabled CNO</td>
</tr>
<tr>
<td>8. Time and Kerberos</td>
<td>Nodes, DNS servers, and DCs agree on time, and the identity can get a ticket</td>
<td>"DNS bad key", "DNS signature failed to verify.", or "DNS signature validity expired."</td>
</tr>
<tr>
<td>9. Change</td>
<td>One link fixed, then the name re-registered</td>
<td>Records deleted, zone loosened, and Repair run in one go</td>
</tr>
<tr>
<td>10. Proof</td>
<td>Every DNS server returns the right address, <code>StatusDNS</code> is 0, and a failover registers cleanly</td>
<td>It works from one DC's DNS but not another's; the event returns on the next move</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which name</strong></td>
<td>The cluster's own name (core resource "Cluster Name", backed by the CNO) or a role's client access point (a file server, SQL Server instance, or other role, backed by a VCO). Different computer objects, possibly different record owners (Steps 6 and 7)</td>
</tr>
<tr>
<td><strong>Which events, with their full text</strong></td>
<td>1195 or 1196 with the reason or code; 1257, 1256, 1258, 1259, 1260, 1261, 1262, 1578, 1579, or 1580; 1069 or 1205 if the resource or role failed outright (Step 2)</td>
</tr>
<tr>
<td><strong>Which node owned the role</strong></td>
<td>The owner node does the registration, so the node in the event matters more than the node you're signed in to (Steps 3 and 4)</td>
</tr>
<tr>
<td><strong>Is the name online?</strong></td>
<td>Online with a warning means clients may still reach the old record; failed means the role is down. That sets your urgency, not your fix</td>
</tr>
<tr>
<td><strong>What resolves today</strong></td>
<td>The address each DNS server returns for the name, and the address the cluster expects (Steps 3 and 10)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>A cluster rebuild with the same name, a node added or rebuilt, a new DC or a retired one, adapter or IP changes, DNS zone changes, scavenging enabled, a record created or deleted by hand, a computer object moved or disabled, a restore</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>1196 with the reason "DNS bad key."</td>
<td>The secure update's signature or key was rejected; often a non-AD DNS server in the node's list, a record the identity can't update, or a Kerberos or time problem</td>
<td>Steps 4, 6, and 8</td>
</tr>
<tr>
<td>1196 "DNS operation refused."</td>
<td>The server refused the update: the zone doesn't take dynamic updates from this client, or the record's ACL blocks it</td>
<td>Steps 5 and 6</td>
</tr>
<tr>
<td>1195 with an error code</td>
<td>The same failure reported as a number; look the number up (9017 is DNS bad key)</td>
<td>Step 2</td>
</tr>
<tr>
<td>1257 "access to update the secure DNS Zone was denied"</td>
<td>Microsoft's guidance: "Ensure that cluster name object (CNO) is granted permissions to the Secure DNS Zone."</td>
<td>Steps 6 and 7</td>
</tr>
<tr>
<td>1580 "due to an existing record with this name"</td>
<td>Microsoft's text: "the cluster identity does not have the sufficient privileges to update that record"</td>
<td>Step 6</td>
</tr>
<tr>
<td>1256 or 1578</td>
<td>The zone doesn't accept dynamic updates, or the node's adapter is pointed at a server that doesn't</td>
<td>Steps 4 and 5</td>
</tr>
<tr>
<td>1258 "a DNS server could not be reached"</td>
<td>The event names the server; the node can't reach it, or it's a server you retired</td>
<td>Step 4</td>
</tr>
<tr>
<td>1259 "failed clean up the existing records"</td>
<td>Old records under the name can't be removed by the cluster's identity</td>
<td>Step 6</td>
</tr>
<tr>
<td>1262 PTR failed</td>
<td>Reverse lookup zone missing or not dynamic, with PTR publishing turned on</td>
<td>Steps 3 and 5</td>
</tr>
<tr>
<td>1193, 1194, 1206, 1207, or "Logon failure: unknown user name or bad password."</td>
<td>A computer object problem, not a DNS one</td>
<td>Step 7</td>
</tr>
<tr>
<td>Registers on one node, fails on another</td>
<td>That node's adapters, suffix, or DNS server list differ</td>
<td>Step 4</td>
</tr>
<tr>
<td>Started right after a cluster rebuild with the same name</td>
<td>The records belong to the old CNO; Microsoft notes "The issue persists even if you destroy the cluster and then rebuild it."</td>
<td>Steps 6 and 7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the event and its reason (read-only)</h2>
<p>Read the events from every node, not just the one you're on. Microsoft's own Event Viewer path is the System log filtered to the FailoverClustering source; this is the same read in PowerShell:</p>
<pre><code class="language-powershell"># Read-only: network name DNS events from the System log on every node
$ids = 1069, 1195, 1196, 1205, 1256, 1257, 1258, 1259, 1260, 1261, 1262, 1578, 1579, 1580
foreach ($n in (Get-ClusterNode).Name) {
    Get-WinEvent -ComputerName $n -MaxEvents 50 -FilterHashtable @{
        LogName      = 'System'
        ProviderName = 'Microsoft-Windows-FailoverClustering'
        Id           = $ids
    } -ErrorAction SilentlyContinue |
        Select-Object @{n='Node';e={$n}}, TimeCreated, Id, Message
}
</code></pre>
<p>Then match the text against Microsoft's failover clustering event list:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Microsoft's message (excerpt)</th>
<th>What to read next (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>1195</td>
<td>"failed registration of one or more associated DNS name(s). The error code was '%2'."</td>
<td>Look up the code (table below)</td>
</tr>
<tr>
<td>1196</td>
<td>"failed registration of one or more associated DNS name(s) for the following reason: %2."</td>
<td>The reason is the DNS error text</td>
</tr>
<tr>
<td>1256</td>
<td>"because the corresponding DNS Zone does not accept dynamic updates."</td>
<td>Zone settings (Step 5)</td>
</tr>
<tr>
<td>1257</td>
<td>"because the access to update the secure DNS Zone was denied."</td>
<td>Record owner and CNO rights (Steps 6 and 7)</td>
</tr>
<tr>
<td>1258</td>
<td>"because the a DNS server could not be reached." (sic)</td>
<td>The DNS server named in the event (Step 4)</td>
</tr>
<tr>
<td>1259</td>
<td>"because the cluster service failed clean up the existing records corresponding to the network name."</td>
<td>Old records and their owner (Step 6)</td>
</tr>
<tr>
<td>1260 and 1261</td>
<td>"Cluster network name resource failed to modify the DNS registration."</td>
<td>The error code or reason in the event</td>
</tr>
<tr>
<td>1262</td>
<td>"failed to publish the PTR record in the DNS reverse lookup zone."</td>
<td>Reverse zone and PublishPTRRecords (Steps 3 and 5)</td>
</tr>
<tr>
<td>1578</td>
<td>"The DNS server may not be configured to accept dynamic updates."</td>
<td>Zone settings and the adapter's server list (Steps 4 and 5)</td>
</tr>
<tr>
<td>1579</td>
<td>"failed to update the DNS record for name '%2' over adapter '%4'."</td>
<td>Record owner (Step 6)</td>
</tr>
<tr>
<td>1580</td>
<td>"This was due to an existing record with this name and the cluster identity does not have the sufficient privileges to update that record."</td>
<td>Record owner (Step 6)</td>
</tr>
</tbody></table>
<p>Two text details matter when you search: Microsoft's 1256 and 1257 text says "associated DNS names(s)", and 1258 says "because the a DNS server could not be reached." Copy the event text as it is rather than retyping it.</p>
<p>The reasons and codes come from the Win32 DNS error list. The ones that show up most in these events:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Microsoft's text</th>
<th>Usually points at (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>9017 (0x2339)</td>
<td>DNS_ERROR_RCODE_BADKEY</td>
<td>"DNS bad key."</td>
<td>The secure update itself: wrong server, record ACL, Kerberos, or time (Steps 4, 6, and 8)</td>
</tr>
<tr>
<td>9016 (0x2338)</td>
<td>DNS_ERROR_RCODE_BADSIG</td>
<td>"DNS signature failed to verify."</td>
<td>Kerberos or time (Step 8)</td>
</tr>
<tr>
<td>9018 (0x233A)</td>
<td>DNS_ERROR_RCODE_BADTIME</td>
<td>"DNS signature validity expired."</td>
<td>Clock skew (Step 8)</td>
</tr>
<tr>
<td>9005 (0x232D)</td>
<td>DNS_ERROR_RCODE_REFUSED</td>
<td>"DNS operation refused."</td>
<td>Zone dynamic update setting or record ACL (Steps 5 and 6)</td>
</tr>
<tr>
<td>9002 (0x232A)</td>
<td>DNS_ERROR_RCODE_SERVER_FAILURE</td>
<td>"DNS server failure."</td>
<td>The DNS server itself, or its AD-integrated zone not loading</td>
</tr>
<tr>
<td>9009 (0x2331)</td>
<td>DNS_ERROR_RCODE_NOTAUTH</td>
<td>"DNS server not authoritative for zone."</td>
<td>The node asked a server that doesn't host the zone (Step 4)</td>
</tr>
<tr>
<td>9010 (0x2332)</td>
<td>DNS_ERROR_RCODE_NOTZONE</td>
<td>"DNS name in update or prereq is not in zone."</td>
<td>Wrong zone for the name's suffix (Step 5)</td>
</tr>
</tbody></table>
<p>Microsoft's older 1196 troubleshooting page also says to "Check the system event log for Netlogon or DNS events that occurred near the time of the failover cluster event." Those are usually the same root cause seen from the node's own registration.</p>
<hr />
<h2>Step 3: Read what the name resource reports (read-only)</h2>
<p>Find every network name, its state, and the node that owns it. The core cluster name lives in the group Microsoft's tools call Cluster Core Resources; roles carry their own:</p>
<pre><code class="language-powershell"># Read-only: every Network Name resource, its role, state, and owner node
Get-ClusterResource |
    Where-Object { $_.ResourceType.Name -eq 'Network Name' } |
    Select-Object Name, OwnerGroup, State, OwnerNode

# The IP address resources each name depends on (an OR list in multi-subnet clusters)
Get-ClusterResourceDependency -Resource 'Cluster Name'
Get-ClusterResourceDependency -Resource 'FS01'
</code></pre>
<p>Then read the private parameters that drive DNS registration. <code>Get-ClusterParameter</code> is Microsoft's tool for this: used with a Network Name resource, "you can obtain DNS-related information about the resource."</p>
<pre><code class="language-powershell"># Read-only: the DNS-related parameters of one network name
Get-ClusterResource -Name 'Cluster Name' |
    Get-ClusterParameter -Name DnsName, HostRecordTTL, RegisterAllProvidersIP, PublishPTRRecords, StatusDNS, LastDNSUpdateTime, CreatingDC

# The last DNS update time is a FILETIME; this turns it into a local date and time
$p = Get-ClusterResource -Name 'Cluster Name' | Get-ClusterParameter -Name LastDNSUpdateTime
[DateTime]::FromFileTime([int64]$p.Value)
</code></pre>
<p>Read the values against Microsoft's definitions:</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Microsoft's definition</th>
<th>What to look for (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>DnsName</td>
<td>"The full DNS label associated with the Netname resource."</td>
<td>The label you expect to see in DNS; the blog adds "It cannot be more than 63 characters in length."</td>
</tr>
<tr>
<td>StatusDNS</td>
<td>"The returned status code from the last DNS operation that the NetName resource performed."</td>
<td>0 means the last operation succeeded; 9017 is DNS bad key, 9005 is refused (Step 2's table)</td>
</tr>
<tr>
<td>LastDNSUpdateTime</td>
<td>"The Last time, in 100 nanosecond units, recorded by the Netname registered DNS."</td>
<td>A date from before the problem started means nothing has registered since</td>
</tr>
<tr>
<td>HostRecordTTL</td>
<td>"Time To Live (TTL) in seconds to be set for the resource records published for the cluster name."</td>
<td>Default 1200; a long TTL explains clients that keep the old address after a fix</td>
</tr>
<tr>
<td>RegisterAllProvidersIP</td>
<td>"Setting this private property to 1 registers the IP addresses of all the provider IP address resources, independent of the online status of any individual provider IP address resource."</td>
<td>Set to 1 in a multi-subnet cluster, you should see every subnet's address in DNS, not just the online one</td>
</tr>
<tr>
<td>PublishPTRRecords</td>
<td>"Determines if PTR resource records, apart from A or AAAA, are published for the Cluster Name."</td>
<td>1 with no dynamic reverse zone explains Event 1262</td>
</tr>
<tr>
<td>CreatingDC</td>
<td>Listed by Microsoft as a Network Name private property</td>
<td>The DC the computer object was created on; useful when replication is in doubt (Step 7)</td>
</tr>
</tbody></table>
<p>Also check the IP address resources the name depends on. Microsoft's dependency example shows the name coming online when "EITHER" provider address does, so a name can be online while one address is offline and never registered:</p>
<pre><code class="language-powershell"># Read-only: state and address of the IP resources behind the name
Get-ClusterResource |
    Where-Object { $_.ResourceType.Name -eq 'IP Address' } |
    ForEach-Object {
        $a = $_ | Get-ClusterParameter -Name Address
        [pscustomobject]@{ Resource = $_.Name; State = $_.State; Group = $_.OwnerGroup; Address = $a.Value }
    }
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>StatusDNS 0 and LastDNSUpdateTime recent</td>
<td>The last attempt worked; the event is old, or another node or name is failing</td>
</tr>
<tr>
<td>StatusDNS non-zero on every name</td>
<td>A node-wide or zone-wide cause: DNS servers, zone settings, or time (Steps 4, 5, and 8)</td>
</tr>
<tr>
<td>StatusDNS non-zero on one name only</td>
<td>That name's record or computer object (Steps 6 and 7)</td>
</tr>
<tr>
<td>An IP address resource offline or failed</td>
<td>Fix the address first (network, subnet, or IP conflict); the name can't register an address that isn't online</td>
</tr>
<tr>
<td>Name online on one node, failing after a move</td>
<td>Read Step 4 on the node it fails on</td>
</tr>
<tr>
<td>Distributed Network Name (DNN) instead of Network Name</td>
<td>A different resource type with its own properties (Microsoft describes a DNN as "an access point used by clients to connect to a Scale-Out File Server."); read its parameters the same way</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Which DNS servers does the owner node ask? (read-only)</h2>
<p>Microsoft's 1196 text is a DNS client instruction: "Ensure that the network adapters associated with dependent IP address resources are configured with at least one accessible DNS server." Read the adapters on every node, because the owner node does the work:</p>
<pre><code class="language-powershell"># Read-only: DNS servers, registration settings, and suffix per adapter, on every node
foreach ($n in (Get-ClusterNode).Name) {
    Invoke-Command -ComputerName $n -ScriptBlock {
        $c = Get-DnsClient
        Get-DnsClientServerAddress -AddressFamily IPv4 |
            Where-Object { $_.ServerAddresses } |
            ForEach-Object {
                $i = $_.InterfaceIndex
                $d = $c | Where-Object InterfaceIndex -eq $i
                [pscustomobject]@{
                    Node      = $env:COMPUTERNAME
                    Adapter   = $_.InterfaceAlias
                    Servers   = $_.ServerAddresses -join ', '
                    Register  = $d.RegisterThisConnectionsAddress
                    UseSuffix = $d.UseSuffixWhenRegistering
                    Suffix    = $d.ConnectionSpecificSuffix
                }
            }
    }
}
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Every node lists the same DNS servers, all of them DCs or DNS servers that host your AD zones</td>
<td>Good; move on to Step 5</td>
</tr>
<tr>
<td>A node lists a public resolver, a firewall, or an ISP server, even second or third</td>
<td>A server that doesn't host your zone can't take a secure update for it; this is a common source of "DNS bad key" and refused updates. Use forwarders on your DNS servers instead (<a href="https://dev.to/adminpackstudio/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-57fl">Windows DNS Server Not Resolving External Names</a>)</td>
</tr>
<tr>
<td>A node lists a DC that's been retired or is down</td>
<td>Event 1258 names it; the node waits on it before it tries another</td>
</tr>
<tr>
<td>Nodes differ from each other</td>
<td>Registration succeeds on some owners and fails on others; that's the "works until it fails over" pattern</td>
</tr>
<tr>
<td>Register is False on the cluster-facing adapter</td>
<td>Microsoft's blog: "If this check box is turned off, the network name doesn't register in DNS for both the Primary DNS suffix and the connection specific DNS suffix."</td>
</tr>
<tr>
<td>A connection-specific suffix with UseSuffix True</td>
<td>The name also registers in that suffix's zone, so that zone has to accept the update too</td>
</tr>
<tr>
<td>Storage, live migration, or heartbeat adapters list DNS servers</td>
<td>Usually not the adapters behind the name's IP resources; leave them unless they carry the name's address (our reading)</td>
</tr>
</tbody></table>
<p>Then prove the node can reach and query each server it lists, by name and by the zone's SOA:</p>
<pre><code class="language-powershell"># Read-only: from the owner node, ask each listed DNS server about the zone and the name
$zone = 'contoso.com'
$name = 'CLUSTER1.contoso.com'
foreach ($s in '10.0.0.10', '10.0.0.11') {
    Test-NetConnection -ComputerName $s -Port 53 |
        Select-Object ComputerName, TcpTestSucceeded
    Resolve-DnsName -Name $zone -Type SOA -Server $s -DnsOnly -ErrorAction SilentlyContinue |
        Select-Object Name, PrimaryServer
    Resolve-DnsName -Name $name -Type A -Server $s -DnsOnly -ErrorAction SilentlyContinue |
        Select-Object Name, IPAddress, TTL
}
</code></pre>
<p>A server that can't return the zone's SOA isn't a server this node should be updating through (our reading). Microsoft's dynamic update article explains why the SOA matters: for a directory-integrated zone, "any DNS server that is loading the zone can respond and dynamically insert its own name as the primary server of the zone in the SOA query response."</p>
<hr />
<h2>Step 5: Does the zone accept the update? (read-only)</h2>
<p>The name registers in a predictable zone. From Microsoft's failover clustering blog: "The registration for a cluster network name always happens in the zone corresponding to the Primary DNS Suffix of the host." Check the node's primary suffix, then the zone on each DNS server:</p>
<pre><code class="language-powershell"># Read-only: the primary DNS suffix of each node (the zone the name registers in)
foreach ($n in (Get-ClusterNode).Name) {
    Invoke-Command -ComputerName $n -ScriptBlock {
        [pscustomobject]@{
            Node   = $env:COMPUTERNAME
            Domain = (Get-CimInstance -ClassName Win32_ComputerSystem).Domain
            Suffix = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters').Domain
        }
    }
}

# Read-only: zone type, AD integration, and dynamic update setting on each DNS server
foreach ($s in 'DC01', 'DC02') {
    Get-DnsServerZone -Name 'contoso.com' -ComputerName $s |
        Select-Object @{n='Server';e={$s}}, ZoneName, ZoneType, IsDsIntegrated, DynamicUpdate, ReplicationScope
}
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>DynamicUpdate Secure, IsDsIntegrated True</td>
<td>The normal, safe setting; registration then depends on the record's ACL (Step 6)</td>
</tr>
<tr>
<td>DynamicUpdate None</td>
<td>Event 1256 or 1578. Microsoft's guidance: "Ensure that the DNS is configured as a Dynamic DNS zone. If the DNS server does not accept dynamic updates uncheck the 'Register this connection's' addresses in DNS' in the properties of the network adapter." Pick one on purpose: a dynamic zone, or static records you maintain</td>
</tr>
<tr>
<td>DynamicUpdate NonsecureAndSecure</td>
<td>Updates are accepted without security; registration should work, and anyone can overwrite the name. Not a setting to adopt as the fix (see the change-vs-avoid table)</td>
</tr>
<tr>
<td>Zone not AD-integrated</td>
<td>Microsoft: "The secure dynamic update functionality is supported only for Active Directory-integrated zones."</td>
</tr>
<tr>
<td>Zone missing on one DNS server</td>
<td>That server can't take the update; the node may be asking it (Step 4)</td>
</tr>
<tr>
<td>Primary suffix differs between nodes, or doesn't match the zone</td>
<td>The name registers in different zones depending on the owner node; fix the suffix, not the zone</td>
</tr>
<tr>
<td>A reverse zone is missing and PublishPTRRecords is 1</td>
<td>Event 1262 only; the A record can still be fine</td>
</tr>
</tbody></table>
<p>If scavenging is on, read the record's timestamp too (Step 6). Microsoft's scavenging article: "Windows clients dynamically update DNS every 24 hours. All DDNS records are set to scavenge." A cluster name that can't refresh because of a 1257 can then age out and disappear, which turns a warning into an outage (<a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers the aging rules).</p>
<hr />
<h2>Step 6: Who owns the existing record? (read-only)</h2>
<p>This is the step most 1257, 1259, 1579, and 1580 events end at. Microsoft's dynamic update article states the default policy: clients "try to overwrite a previously registered resource record, unless they are specifically blocked by update security." In a secure zone, the record's ACL is that security. Read the record first:</p>
<pre><code class="language-powershell"># Read-only: the record as each DNS server holds it (Timestamp empty or 0 = static)
foreach ($s in 'DC01', 'DC02') {
    Get-DnsServerResourceRecord -ZoneName 'contoso.com' -Name 'CLUSTER1' -ComputerName $s -ErrorAction SilentlyContinue |
        Select-Object @{n='Server';e={$s}}, HostName, RecordType, Timestamp, TimeToLive,
            @{n='Data';e={$_.RecordData.IPv4Address}}
}
</code></pre>
<p>Then read its owner and ACL. In an AD-integrated zone the record is a <code>dnsNode</code> object; this reads it without changing anything:</p>
<pre><code class="language-powershell"># Read-only: owner and ACL entries of the record object (domain-wide replication scope shown)
$dom = (Get-ADDomain).DistinguishedName
$rec = Get-ADObject -Server DC01 -SearchBase "DC=DomainDnsZones,$dom" `
    -LDAPFilter '(&amp;(objectClass=dnsNode)(name=CLUSTER1))' -Properties nTSecurityDescriptor, whenCreated
$rec | Select-Object DistinguishedName, whenCreated
$rec.nTSecurityDescriptor.Owner
$rec.nTSecurityDescriptor.Access |
    Where-Object { $_.IdentityReference -match 'CLUSTER1|Authenticated Users|Everyone|DnsUpdateProxy' } |
    Select-Object IdentityReference, ActiveDirectoryRights, AccessControlType
</code></pre>
<p>In DNS Manager, the same read is the record's <strong>Properties &gt; Security</strong> tab (on older consoles, <strong>View &gt; Advanced</strong> first). Read the results like this:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>No record exists</td>
<td>The cluster should be able to create one; Microsoft's blog: "By default, all Authenticated Users have permissions to create a new record inside a secure zone." If it still fails, the cause is the zone, the server, or time (Steps 4, 5, and 8)</td>
</tr>
<tr>
<td>Owner is the CNO (or the role's computer object) and it has Full Control</td>
<td>Ownership is fine; look at Steps 7 and 8</td>
</tr>
<tr>
<td>Owner is a user or an admin account, Timestamp empty</td>
<td>Somebody created a static record by hand; the cluster's identity has no rights on it. The blog: "if a record already exists, the security principal (in this case the cluster name identity) should have Full Control over the existing DNS record."</td>
</tr>
<tr>
<td>Owner is a SID that doesn't resolve</td>
<td>The identity that created it is gone: often the CNO of a cluster you destroyed and rebuilt with the same name (the new CNO is a new object with a new SID)</td>
</tr>
<tr>
<td>Owner is a DHCP server or the DnsUpdateProxy group</td>
<td>DHCP registered the address before the cluster did. Microsoft explains the general rule: "After the DHCP server becomes the owner of the client name, only that DHCP server can update the name."</td>
</tr>
<tr>
<td>The record exists in one DNS partition and the zone in another</td>
<td>A leftover copy from an old replication scope; read both partitions before you assume which one clients get</td>
</tr>
<tr>
<td>Record timestamp old, zone scavenging on</td>
<td>It's refreshing on time or it's about to be scavenged (Step 5)</td>
</tr>
</tbody></table>
<p>For a zone stored in the forest-wide partition, use <code>DC=ForestDnsZones</code> and the forest root in <code>-SearchBase</code> instead. For a role's name (FS01, a SQL Server instance), repeat with that name; Microsoft's 1580 text refers to "the cluster identity" and its record, so compare the record's owner with both the CNO and the role's own computer object rather than guessing which one should be there.</p>
<hr />
<h2>Step 7: Is the computer object behind the name healthy? (read-only)</h2>
<p>The network name acts as a computer account in AD: the CNO for the cluster name, and a VCO for each role's client access point. Microsoft's description: "Through the CNO, virtual computer objects (VCOs) are automatically created when you set up clustered roles that use client access points." Read both:</p>
<pre><code class="language-powershell"># Read-only: the CNO and a VCO, as one DC sees them
Get-ADComputer -Identity 'CLUSTER1' -Server DC01 -Properties Enabled, DistinguishedName, whenCreated, PasswordLastSet, ObjectGUID |
    Select-Object Name, Enabled, DistinguishedName, whenCreated, PasswordLastSet, ObjectGUID
Get-ADComputer -Identity 'FS01' -Server DC01 -Properties Enabled, DistinguishedName, PasswordLastSet |
    Select-Object Name, Enabled, DistinguishedName, PasswordLastSet

# The GUID the name resource expects, to compare with the AD object above
Get-ClusterResource -Name 'Cluster Name' | Get-ClusterParameter -Name ObjectGUID
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Enabled True, GUIDs match, PasswordLastSet recent</td>
<td>The object is healthy; the failure is DNS-side (Steps 4 to 6 and 8)</td>
</tr>
<tr>
<td>Enabled False</td>
<td>Microsoft's first resolution step for the rebuilt-cluster 1257 case is to "Verify that the CNO exists in Active Directory, and that it isn't disabled." A prestaged CNO is created disabled on purpose, but a running cluster's CNO shouldn't stay that way</td>
</tr>
<tr>
<td>Object not found</td>
<td>Deleted or moved to a container the cluster can't see; restore it from the AD Recycle Bin if it's there rather than creating a new one with the same name</td>
</tr>
<tr>
<td>GUIDs differ</td>
<td>The AD object with that name isn't the one the cluster created; often a rebuild or a hand-made replacement, and the DNS record ACL usually points at the old one (Step 6)</td>
</tr>
<tr>
<td>Event text "Logon failure: unknown user name or bad password."</td>
<td>Microsoft: "the password for the cluster name account no longer matches the corresponding password stored by the clustering software." Repair Active Directory Object is the documented fix (Step 9)</td>
</tr>
<tr>
<td>Events 1193 or 1194 for a role</td>
<td>The CNO couldn't create the VCO. Microsoft's checklist: the cluster identity "can create computer objects", "The quota for computer objects has not been reached.", and an existing object grants the cluster identity "'Full Control' permission to that computer object"</td>
</tr>
<tr>
<td>Events 1206 or 1207</td>
<td>Microsoft: "The cluster identity '%5' may lack permissions required to update the object."</td>
</tr>
<tr>
<td>Events 1211 or 1212</td>
<td>The node couldn't find a writable DC; Microsoft's guidance adds "Also ensure that the DNS server is running in order to resolve the name of the domain controller."</td>
</tr>
</tbody></table>
<p>The rights Microsoft documents for these objects:</p>
<table>
<thead>
<tr>
<th>Object</th>
<th>Right</th>
<th>Where Microsoft says it</th>
</tr>
</thead>
<tbody><tr>
<td>CNO on its OU or container</td>
<td>"Create Computer objects and Read all properties"</td>
<td>Troubleshoot issues with accounts used by failover clusters</td>
</tr>
<tr>
<td>CNO on each VCO</td>
<td>"Full control"</td>
<td>Same article; and Prestage cluster computer objects</td>
</tr>
<tr>
<td>CNO on the secure DNS zone (rebuilt-cluster 1257 case)</td>
<td>"the Create all child objects and Write all properties permissions"</td>
<td>Cluster role doesn't come online after you rebuild the cluster</td>
</tr>
<tr>
<td>Cluster identity on an existing DNS record</td>
<td>"Full Control over the existing DNS record"</td>
<td>DNS Registration with the Network Name Resource (failover clustering blog)</td>
</tr>
<tr>
<td>Default Computers container</td>
<td>"a cluster administrator can create up to 10 VCOs without any additional configuration"</td>
<td>Prestage cluster computer objects</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: Is time or Kerberos in the way? (read-only)</h2>
<p>A secure update is signed with the identity's Kerberos credentials, so "DNS bad key", "DNS signature failed to verify.", and "DNS signature validity expired." can be identity or clock problems rather than DNS ones (our reading). Read time and tickets from the owner node:</p>
<pre><code class="language-powershell"># Read-only: time offset of each node against a DC, and the node's own secure channel
foreach ($n in (Get-ClusterNode).Name) {
    Invoke-Command -ComputerName $n -ScriptBlock {
        w32tm /stripchart /computer:DC01 /samples:3 /dataonly
        w32tm /query /status
    }
}
</code></pre>
<pre><code class="language-console">REM On the owner node, elevated: tickets held by the computer (SYSTEM) logon session
klist -li 0x3e7
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Offsets of a few seconds</td>
<td>Not the cause</td>
</tr>
<tr>
<td>A node minutes off, or syncing from its host or a local clock</td>
<td>Fix time first; <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> covers the w32tm and VM integration chain</td>
</tr>
<tr>
<td>Kerberos errors in the System log near the 1196 (for example, the target principal name is incorrect)</td>
<td>The DNS server's or DC's identity doesn't match its name; <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> covers that chain</td>
</tr>
<tr>
<td>The DNS server in the node's list is a non-Windows server for your AD zone</td>
<td>Secure updates may not be supported there at all; Microsoft's older 1196 page notes a hotfix for third-party BIND servers on Windows Server 2008 R2</td>
</tr>
<tr>
<td>Time and Kerberos both clean</td>
<td>Go back to Steps 4 and 6; BADKEY is then most often the server or the record</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Change one link, then re-register (a change)</h2>
<p>Fix only what Steps 2 to 8 showed, then re-register the name and read the result before changing anything else.</p>
<p><strong>The node's DNS server list (Step 4).</strong> Point every cluster-facing adapter at your AD DNS servers only, the same list on every node:</p>
<pre><code class="language-powershell"># A change, on the node that was wrong: replace its DNS server list on one adapter
Set-DnsClientServerAddress -InterfaceAlias 'Mgmt' -ServerAddresses '10.0.0.10', '10.0.0.11'

# Re-read it
Get-DnsClientServerAddress -InterfaceAlias 'Mgmt' -AddressFamily IPv4
</code></pre>
<p><strong>The adapter's registration setting (Step 4).</strong> If the cluster-facing adapter had registration turned off and the zone is dynamic, turn it back on for that adapter (<code>Set-DnsClient -InterfaceAlias 'Mgmt' -RegisterThisConnectionsAddress $true</code>). If the zone is meant to be static, leave it off and maintain the records yourself, on purpose.</p>
<p><strong>The zone's dynamic update setting (Step 5).</strong> If an AD-integrated zone was set to None by mistake, set it to secure only, not to nonsecure. Microsoft's <code>-DynamicUpdate</code> values are None, Secure, and NonsecureAndSecure:</p>
<pre><code class="language-powershell"># A change: AD-integrated zone back to secure dynamic updates only
Set-DnsServerPrimaryZone -Name 'contoso.com' -DynamicUpdate Secure -ComputerName DC01
</code></pre>
<p><strong>The record's ACL (Step 6).</strong> For an existing record the cluster can't update, the narrow fix is the record, not the zone: in DNS Manager, open the record's <strong>Properties &gt; Security</strong>, add the cluster's computer object (CLUSTER1$; enable <strong>Computers</strong> in Object Types), and give it <strong>Full control</strong>, as Microsoft's blog describes. For the rebuilt-cluster case, Microsoft's documented resolution is the zone-level grant: on the zone's <strong>Properties &gt; Security</strong>, "Verify that the CNO has the Create all child objects and Write all properties permissions. If the permissions are missing, configure them."</p>
<p><strong>A stale record you've decided to remove (Step 6).</strong> Only when its owner is gone or wrong and the cluster can't take it over. Microsoft's <code>Remove-DnsServerResourceRecord</code> deletes "all records that match RRtype and Name" unless you give <code>-RecordData</code>, so name the address:</p>
<pre><code class="language-powershell"># A change: remove one stale A record by its address, previewing first
Remove-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'A' -Name 'CLUSTER1' -RecordData '10.0.0.50' -ComputerName DC01 -WhatIf
Remove-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'A' -Name 'CLUSTER1' -RecordData '10.0.0.50' -ComputerName DC01
</code></pre>
<p>Clients that cached the old record keep it for its TTL, and the name doesn't resolve until the cluster re-registers, so do this in a window and re-register right away.</p>
<p><strong>The computer object (Step 7).</strong> If the object is disabled and it's the running cluster's CNO, enable it (<code>Enable-ADAccount -Identity 'CLUSTER1$'</code>). For the password mismatch, use Microsoft's procedure in Failover Cluster Manager: expand <strong>Cluster Core Resources</strong>, under <strong>Cluster Name</strong> right-click the <strong>Name</strong> item and select <strong>Take Offline</strong>, then <strong>More Actions &gt; Repair Active Directory Object</strong>. Microsoft notes that "Taking the cluster's Name resource offline shouldn't negatively impact other cluster groups, such as the SQL Server Failover Cluster Instance." Bring the name back online afterwards. Microsoft's account troubleshooting article also covers the CNO's rights on its container and on each VCO, and the computer account quota (ms-DS-MachineAccountQuota) when VCO creation fails.</p>
<p><strong>Re-register the name.</strong> Use the online path first, then read the status:</p>
<pre><code class="language-powershell"># A change, without taking the name offline: re-register network names in DNS
Get-ClusterResource -Name 'Cluster Name' | Update-ClusterNetworkNameResource
Get-ClusterResource -Name 'FS01' | Update-ClusterNetworkNameResource

# Read the result
Get-ClusterResource -Name 'Cluster Name' | Get-ClusterParameter -Name StatusDNS, LastDNSUpdateTime
</code></pre>
<p>If it still fails, take that one name offline and back online in a window (<code>Stop-ClusterResource -Name 'FS01'</code> then <code>Start-ClusterResource -Name 'FS01'</code>). Microsoft's <code>Stop-ClusterResource</code> example notes: "Before taking the resource offline, it takes offline any dependent resources." For a role's name, that means the role's clients lose it, so read the dependency report first. Taking a name offline doesn't clean DNS either; the blog: "Offlining a network name resources does not delete the DNS records on the server. They are only cleaned up when a network name resource is deleted from the cluster."</p>
<p>When the event is gone, collect the logs anyway if it took more than one change; Microsoft's data collection step for this issue is <code>Get-ClusterLog -UseLocalTime -Destination</code> with a folder:</p>
<pre><code class="language-powershell"># Read-only: cluster log from every node for the last 30 minutes, local time, copied here
Get-ClusterLog -UseLocalTime -TimeSpan 30 -Destination C:\Temp\ClusterLogs
Select-String -Path C:\Temp\ClusterLogs\*.log -Pattern 'DNS' | Select-Object -Last 40
</code></pre>
<hr />
<h2>Step 10: Prove every DNS server answers with the right address (read-only)</h2>
<p>Ask every DNS server that clients use, not just the one the node used, and then make the cluster register from another node:</p>
<pre><code class="language-powershell"># Read-only: what each DNS server returns for each network name
$names   = 'CLUSTER1.contoso.com', 'FS01.contoso.com'
$servers = '10.0.0.10', '10.0.0.11', '10.1.0.10'
foreach ($s in $servers) {
    foreach ($nm in $names) {
        Resolve-DnsName -Name $nm -Type A -Server $s -DnsOnly -ErrorAction SilentlyContinue |
            Select-Object @{n='Server';e={$s}}, Name, IPAddress, TTL
    }
}

# Read-only: the cluster's own view after the change
Get-ClusterResource | Where-Object { $_.ResourceType.Name -eq 'Network Name' } | ForEach-Object {
    $st = $_ | Get-ClusterParameter -Name StatusDNS
    [pscustomobject]@{ Name = $_.Name; State = $_.State; Owner = $_.OwnerNode; StatusDNS = $st.Value }
}
</code></pre>
<p>Then, in a window, move the role to each other node and read the events and <code>StatusDNS</code> again. Microsoft's own resolution for the rebuild case ends the same way: "To test the cluster, perform a manual failover." and "Review the event logs to verify that DNS registration succeeds and doesn't generate permission errors."</p>
<pre><code class="language-powershell"># A deliberate move (the role restarts on the other node), then the same reads
Move-ClusterGroup -Name 'FS01' -Node HVN01
Get-ClusterResource -Name 'FS01' | Get-ClusterParameter -Name StatusDNS, LastDNSUpdateTime
</code></pre>
<p>Pass criteria (our reading): every DNS server returns the expected addresses (all subnets if RegisterAllProvidersIP is 1); <code>StatusDNS</code> is 0 for every network name; LastDNSUpdateTime is after your change; no new 1195, 1196, 1257, 1258, 1259, 1578, 1579, or 1580 on any node after a move; the record's owner and ACL include the cluster's identity; and <code>Test-Cluster -Include 'Network'</code> (Microsoft lists Network among the validation test types) runs clean if you changed adapters.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it doesn't help (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Running <code>ipconfig /registerdns</code> on the node</td>
<td>Microsoft describes it as a way "to manually force an update of the client name registration in DNS." It's the node's own name, not the cluster's network names</td>
</tr>
<tr>
<td>Deleting every record for the name "to be safe"</td>
<td>Without <code>-RecordData</code>, removal deletes all records matching the name and type; clients lose the name until the cluster re-registers, and the cause (server, zone, or identity) is still there</td>
</tr>
<tr>
<td>Giving Authenticated Users or Everyone full control of the zone</td>
<td>Fixes the symptom by letting any account overwrite any record in the zone</td>
</tr>
<tr>
<td>Switching the zone to nonsecure updates</td>
<td>Same: every client can then overwrite the name, including the cluster's</td>
</tr>
<tr>
<td>Running Repair for a 1257</td>
<td>Repair Active Directory Object is Microsoft's fix for the cluster name account's password; it doesn't change the record's ACL</td>
</tr>
<tr>
<td>Recreating the CNO by hand with the same name</td>
<td>A new object with a new SID; the old record ACL and the cluster's stored GUID still point at the old one</td>
</tr>
<tr>
<td>Creating a static A record so it resolves</td>
<td>Works until the next failover or address change, and the cluster's identity can't update it unless you grant it rights (Microsoft's 1196 page does say to recreate a deleted or scavenged record; if you do, give the cluster's identity Full Control on it)</td>
</tr>
<tr>
<td>Testing from the node you're signed in to</td>
<td>The owner node registers; test the node in the event</td>
</tr>
<tr>
<td>Reading one DC's DNS</td>
<td>Each DNS server answers from its own copy; replication may not have arrived yet</td>
</tr>
<tr>
<td>Lowering HostRecordTTL as the fix</td>
<td>Changes how fast clients notice a correct record; it doesn't make registration succeed</td>
</tr>
<tr>
<td>Clearing the client DNS cache and calling it fixed</td>
<td><code>Clear-DnsClientCache</code> changes what one client remembers, not what the server holds</td>
</tr>
</tbody></table>
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>One node lists a public or retired DNS server</td>
<td>The same AD DNS servers on every node's cluster-facing adapters</td>
<td>Adding more DNS servers to the list until one works</td>
</tr>
<tr>
<td>Zone set to None by mistake</td>
<td>Secure only on the AD-integrated zone</td>
<td>NonsecureAndSecure as the fix</td>
</tr>
<tr>
<td>Hand-made or DHCP-owned record blocks the update</td>
<td>Full control for the cluster's computer object on that record</td>
<td>Full control for Authenticated Users or Everyone on the zone</td>
</tr>
<tr>
<td>Rebuilt cluster with the same name, Event 1257</td>
<td>Microsoft's grant to the new CNO on the zone, or a record ACL that names the new CNO</td>
<td>Recreating the CNO by hand to match the old SID</td>
</tr>
<tr>
<td>Stale record whose owner is gone</td>
<td>Remove that record by its address in a window, then re-register at once</td>
<td>Removing every record for the name without <code>-RecordData</code></td>
</tr>
<tr>
<td>CNO disabled</td>
<td>Enable it, then read the object's location and rights</td>
<td>Creating a second object with the same name</td>
</tr>
<tr>
<td>"Logon failure: unknown user name or bad password."</td>
<td>Take the cluster Name offline and run Repair Active Directory Object</td>
<td>Repair as a fix for DNS ACL errors</td>
</tr>
<tr>
<td>Node clock off</td>
<td>Fix time on the node and its source first</td>
<td>Loosening DNS security to work around a Kerberos failure</td>
</tr>
<tr>
<td>Name registered but clients still get the old address</td>
<td>Wait out or lower HostRecordTTL on purpose, and flush the clients that matter</td>
<td>Deleting and recreating the record again</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does "Cluster network name resource failed registration of one or more associated DNS name(s)" mean?</strong>
The network name couldn't create or update its DNS records from the node that owns it. Event 1196 adds the DNS error as text after "for the following reason:", and Event 1195 gives it as a code. Read that reason, the name's <code>StatusDNS</code>, and the owner node's DNS server list before you change anything (Steps 2 to 4).</p>
<p><strong>How do I fix Event 1196 "DNS bad key" on a failover cluster?</strong>
Check, in order: that every node's cluster-facing adapter lists only your AD DNS servers; that the zone is AD-integrated and set to secure updates; that the existing record grants the cluster's computer object rights to update it; and that the node's clock and Kerberos are healthy. Fix the one that's wrong, then run <code>Update-ClusterNetworkNameResource</code> and read <code>StatusDNS</code> again (Steps 4 to 9).</p>
<p><strong>What permissions does the cluster name object need on DNS for Event 1257?</strong>
Microsoft's guidance in the event is "Ensure that cluster name object (CNO) is granted permissions to the Secure DNS Zone." For a rebuilt cluster, its resolution is the zone-level "Create all child objects and Write all properties" for the CNO; for one existing record, the narrower fix is Full control for the cluster's identity on that record (Steps 6 and 9).</p>
<p><strong>Does Repair Active Directory Object fix DNS registration?</strong>
Not by itself. Microsoft documents Repair for the case where "the password for the cluster name account no longer matches the corresponding password stored by the clustering software." It's greyed out until the cluster Name resource is offline. A 1257 caused by a record ACL needs the ACL fixed (Steps 7 and 9).</p>
<p><strong>How do I re-register a cluster name in DNS without downtime?</strong>
Run <code>Get-ClusterResource -Name 'Cluster Name' | Update-ClusterNetworkNameResource</code> (or the role's name), which Microsoft says works "in a way that doesn't interrupt cluster availability." Then read <code>StatusDNS</code> and <code>LastDNSUpdateTime</code>, and ask every DNS server for the name (Steps 9 and 10).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> (when the cluster name's record was aged out and deleted)</li>
<li><a href="https://dev.to/adminpackstudio/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-57fl">Windows DNS Server Not Resolving External Names</a> (when nodes list a public resolver because the forwarders were wrong)</li>
<li><a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> (when the secure update fails on Kerberos between the node and the DNS server)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (when "DNS bad key" or "signature validity expired" comes from clock skew)</li>
</ul>
<h2>Free checklist</h2>
<p>Giving the whole zone to Authenticated Users, or switching it to nonsecure updates, before anyone read which record and which identity failed, is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=cluster-network-name-failed-registration">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=cluster-network-name-failed-registration</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=cluster-network-name-failed-registration">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=cluster-network-name-failed-registration</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 and 10 are read-only checks: <code>Get-WinEvent</code>, <code>Get-ClusterNode</code>, <code>Get-ClusterResource</code>, <code>Get-ClusterParameter</code>, <code>Get-ClusterResourceDependency</code>, <code>Get-DnsClient</code>, <code>Get-DnsClientServerAddress</code>, <code>Test-NetConnection</code>, <code>Resolve-DnsName</code>, <code>Get-DnsServerZone</code>, <code>Get-DnsServerResourceRecord</code>, <code>Get-ADObject</code>, <code>Get-ADComputer</code>, <code>w32tm /stripchart</code> and <code>/query /status</code>, <code>klist</code>, and <code>Get-ClusterLog</code>. Changing DNS server lists or adapter registration, zone dynamic update settings, record or zone permissions, removing records, enabling or repairing computer objects, re-registering or restarting network names, and moving roles are deliberate changes. Change one link at a time, prove it from every DNS server and every node, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[FSMO Role Transfer Failed: A Read-First Owner, Replication, Permission, and Seize-or-Wait Triage]]></title><description><![CDATA[FSMO role transfer failed, or about to seize? Read the owners from every DC, replication, your groups, and the old DC's future before you run seize.]]></description><link>https://adminpackstudio.hashnode.dev/fsmo-role-transfer-failed-a-read-first-owner-replication-permission-and-seize-or-wait-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/fsmo-role-transfer-failed-a-read-first-owner-replication-permission-and-seize-or-wait-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 10:42:08 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "DC01 died Saturday night (the vendor says the replacement board won't arrive before Thursday). Since then nobody can create new user accounts on one of the remaining DCs, and a password reset took ages to show up. Someone tried to move the roles to DC02 in Active Directory Users and Computers and got 'The current FSMO holder could not be contacted.' A forum post says to seize all five roles with ntdsutil right now, then just power DC01 back on when the board comes. Can we do that?"</p>
<p>Not that way. <strong>"FSMO role transfer failed" usually isn't a broken role. A transfer is a conversation between the current holder and the new one, so it fails whenever the current holder is offline, unreachable, not yet replicated since it last started, or recorded as a server that no longer exists; a seizure skips that conversation, which is why it's a one-way decision about the old DC, not a stronger retry.</strong> Microsoft's guidance is blunt about both halves: "Role seizure should be performed only as a last resort", and "Do not allow a former operations master role holder whose role has been seized to return to an online domain controller." Seizing all five roles and then powering DC01 back on is the one combination that page warns about. Microsoft also notes that "FSMO roles assigned to domain controllers that are offline or in an error state only have to be transferred or seized if role-dependent operations are being done", so the first question is which operations are actually failing. Work through it as a chain: <strong>which roles, and what's failing because of them</strong> → <strong>who owns each role, according to more than one DC?</strong> → <strong>is the current holder online and replicating?</strong> → <strong>is the target DC the right one?</strong> → <strong>does your account have the rights for this role?</strong> → <strong>transfer, wait, or seize?</strong> → <strong>transfer it properly</strong> → <strong>seize it properly, only if Step 6 says so</strong> → <strong>clean up the old DC and the owners that still point at it</strong> → <strong>prove every DC agrees.</strong> <code>netdom query fsmo</code>, <code>Get-ADDomain</code>, <code>Get-ADForest</code>, <code>Get-ADObject</code> on the role objects, <code>dcdiag /test:KnowsOfRoleHolders</code>, <code>repadmin /replsummary</code>, <code>Get-ADReplicationPartnerMetadata</code>, <code>Get-ADDomainController</code>, and <code>whoami /groups</code> are the sources for that chain. Seize every role "to be safe" and you may burn RID space, leave a stale owner in a DNS application partition, and create a duplicate role holder the day the old hardware comes back.</p>
<p>If the role holder can't be reached because replication itself fails with RPC errors, <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> covers that side first. If the new PDC emulator now has to become the forest's time source, <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> covers the time chain. This post covers <strong>"an operations master (FSMO) role won't transfer, or you're deciding whether to seize it: 'The requested FSMO operation failed. The current FSMO holder could not be contacted.', 0x20AF or 0x20AE from ntdsutil or PowerShell, 'Access is denied' when you try, a role owner that shows a deleted DC, a demotion that can't determine role ownership, or a role holder that died and isn't coming back, and you need a read-first path before you run <code>seize</code> or <code>-Force</code>."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Transfer needs the old holder; seize doesn't, and that's the risk.</strong> Microsoft: "We recommend that you only seize all roles when the previous role holder isn't returning to the domain." (Step 6).</li>
<li><strong>Read the owners from more than one DC.</strong> <code>dcdiag</code>'s KnowsOfRoleHolders test "doesn't check all DCs knowledge for consistency", so compare <code>netdom query fsmo</code> and the <code>fSMORoleOwner</code> values from each DC (Step 2).</li>
<li><strong>A holder that just restarted isn't a holder yet.</strong> "The DC waits until it receives inbound replication for its naming context" before acting on a role, and so does a new holder after a transfer (Steps 3 and 10).</li>
<li><strong>Connect ntdsutil to the new holder, not the dead one.</strong> In Microsoft's procedure, the server you connect to "is the name of the DC that you want to assign the Operation Master role to." (Steps 7 and 8).</li>
<li><strong>Seizing the RID master costs RIDs.</strong> "The Move-ADDirectoryServerOperationMasterRole cmdlet increases the next Rid pool by 30,000 from what it finds in Active Directory." (Step 8).</li>
<li><strong>After a seizure, the old DC never comes back as it was.</strong> Clean up its metadata, and rebuild it from scratch if you want it as a DC again (Step 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which roles, which operations fail because of them, which DC you ran the move from and as whom, the full error, and whether the old holder is coming back</td>
<td>"FSMO is broken" with no role, no error text, and no decision about the old DC</td>
</tr>
<tr>
<td>2. Owners read</td>
<td>Every DC names the same owner for each role, and every owner is a live, writable DC</td>
<td>Owners differ between DCs; an owner with a deleted (DEL) name; an owner nobody recognizes</td>
</tr>
<tr>
<td>3. Holder reachable</td>
<td>The current holder is online, answering, and has replicated inbound since it last started</td>
<td>0x20AF "The current FSMO holder could not be contacted."; replication failures on the holder or the target</td>
</tr>
<tr>
<td>4. Target right</td>
<td>The new holder is in the same domain, writable, healthy, up to date, and fits the global catalog rule for the infrastructure master</td>
<td>Transfer to an RODC; a target that's itself failing replication; infrastructure master on a GC in a domain that still needs it</td>
</tr>
<tr>
<td>5. Rights</td>
<td>The account is in the group each role needs, in an elevated session</td>
<td>"Access is denied"; a terminating permission error from PowerShell</td>
</tr>
<tr>
<td>6. Decision</td>
<td>Transfer when the holder is up, wait when nothing needs the role yet, seize only when the holder isn't returning</td>
<td>All five roles seized for a DC that's back in two days</td>
</tr>
<tr>
<td>7. Transfer</td>
<td>The role moves with the holder's cooperation, from the right tool, to the right DC</td>
<td>Transfer run against the dead DC; FQDN passed straight to <code>-Identity</code></td>
</tr>
<tr>
<td>8. Seize</td>
<td>The role is seized on the right DC after replication has caught up, and the RID warning is understood</td>
<td>"Search failed to find any Domain Controllers"; RID pool jump nobody expected</td>
</tr>
<tr>
<td>9. Cleanup</td>
<td>The old DC's metadata is gone, no role owner (including DNS application partitions) points at it, and time follows the new PDC</td>
<td>Demotion or adprep fails later; the old DC returns and claims roles; time drifts</td>
</tr>
<tr>
<td>10. Proof</td>
<td>Every DC reports the new owner and the role-dependent operation works</td>
<td><code>netdom</code> agrees on one DC and not another; account creation still fails</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which roles</strong></td>
<td>Schema master and domain naming master are forest-wide; PDC emulator, RID master, and infrastructure master are per domain. A move of the forest roles needs different groups and is checked against the forest root (Step 5)</td>
</tr>
<tr>
<td><strong>What's failing because of them</strong></td>
<td>Creating users, groups, or computers on a DC whose RID pool runs out (RID master); password changes, lockouts, Group Policy edits, or time (PDC emulator); adding or removing a domain or application partition (domain naming master); schema updates (schema master); cross-domain group member names or <code>adprep /rodcprep</code> (infrastructure master). If nothing is failing, you may have time (Step 6)</td>
</tr>
<tr>
<td><strong>Current holder and its state</strong></td>
<td>Is it powered off, unreachable, booting, restored from backup, force-demoted, or reinstalled? Is it coming back, and when? This is the seize decision (Step 6)</td>
</tr>
<tr>
<td><strong>Target DC</strong></td>
<td>Name, domain, site, global catalog or not, writable or read-only (Step 4)</td>
</tr>
<tr>
<td><strong>Tool, place, and account</strong></td>
<td>Active Directory Users and Computers, Domains and Trusts, the Schema snap-in, ntdsutil, or <code>Move-ADDirectoryServerOperationMasterRole</code>; run on which computer, as which account, elevated or not (Steps 5 and 7)</td>
</tr>
<tr>
<td><strong>Full error text</strong></td>
<td>The whole ntdsutil or PowerShell output, including the hex code (0x20AF, 0x20AE) and any "proceeding with seizure" line (Steps 3 and 8)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>Hardware failure, a forced demotion, a restore, a new DC that reused an old DC's name or IP address, a DNS change, a site or firewall change</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>0x20AF "The requested FSMO operation failed. The current FSMO holder could not be contacted."</td>
<td>The holder is offline, unreachable from the DC doing the work, or hasn't finished inbound replication since it started</td>
<td>Steps 3 and 6</td>
</tr>
<tr>
<td>0x20AE "The role owner attribute could not be read."</td>
<td>The role object's owner value can't be read or resolved on the DC you asked</td>
<td>Step 2</td>
</tr>
<tr>
<td>An owner that contains <code>DEL:</code> or <code>0ADEL</code></td>
<td>The role points at an NTDS Settings object that has been deleted (Microsoft's reading in its seize and demotion articles)</td>
<td>Steps 2 and 9</td>
</tr>
<tr>
<td>"Access is denied"</td>
<td>Wrong group, a non-elevated session, or, during metadata cleanup, objects protected from accidental deletion</td>
<td>Steps 5 and 9</td>
</tr>
<tr>
<td>"Transfer of RID FSMO failed, proceeding with seizure" then "Search failed to find any Domain Controllers"</td>
<td>Microsoft's cause: "The fSMORoleOwner attribute of the RID Manager$ object in Active Directory is invalid."</td>
<td>Step 8</td>
</tr>
<tr>
<td>A demotion fails with "unable to determine the ownership of floating single-master operation roles"</td>
<td>A DNS application partition's infrastructure master points at a deleted DC</td>
<td>Step 9</td>
</tr>
<tr>
<td>Different DCs name different owners</td>
<td>Replication hasn't converged, or a DC is isolated</td>
<td>Steps 2 and 3</td>
</tr>
<tr>
<td>The holder is gone for good (dead hardware, reinstalled, force-demoted)</td>
<td>Seizure is the expected path, after replication checks</td>
<td>Steps 6 and 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read who owns each role, from more than one DC (read-only)</h2>
<p>Start with the two quick reads (Microsoft says <code>netdom query</code> must run from an elevated command prompt), then repeat them against the target DC and at least one other DC. Each DC answers from its own copy of the directory:</p>
<pre><code class="language-powershell"># The role owners as this computer's DC sees them (Microsoft's netdom query fsmo)
netdom query fsmo

# The same question answered by specific DCs (/server is the DC that performs the query)
netdom query /server:DC02 fsmo
netdom query /server:DC03 fsmo

# Domain roles and forest roles, asked of one DC at a time
Get-ADDomain -Server DC02 | Select-Object PDCEmulator, RIDMaster, InfrastructureMaster
Get-ADForest -Server DC02 | Select-Object SchemaMaster, DomainNamingMaster
</code></pre>
<p><code>dcdiag</code> has a test for this, but read its scope: KnowsOfRoleHolders "returns the DCs knowledge of the five Flexible Single Master Operation (FSMO) roles but doesn't check all DCs knowledge for consistency. Using the /e parameter provides data for comparison." The similarly named FsmoCheck test isn't the same thing; Microsoft's description ends with "This test does not test any of the servers for operations master roles."</p>
<pre><code class="language-console">REM Every DC in the enterprise reports what it knows about the five roles; compare the answers
dcdiag /test:KnowsOfRoleHolders /e /v
</code></pre>
<p>When the friendly names look wrong, read the raw owner. Each role is an <code>fSMORoleOwner</code> value on one object, and Microsoft's own instruction for a partition's infrastructure master is to "query the fSMORoleOwner attribute on the infrastructure object under the naming context root in question." The same read works for every role object:</p>
<pre><code class="language-powershell"># Read-only: the raw owner (an NTDS Settings DN) of every role object, as DC02 sees it
$dc   = 'DC02'
$root = Get-ADRootDSE -Server $dc
$dom  = $root.defaultNamingContext
$fr   = $root.rootDomainNamingContext
$objs = @(
    $dom                                          # PDC emulator (domain head)
    "CN=RID Manager`$,CN=System,$dom"             # RID master
    "CN=Infrastructure,$dom"                      # infrastructure master
    "CN=Partitions,$($root.configurationNamingContext)"  # domain naming master
    $root.schemaNamingContext                     # schema master
    "CN=Infrastructure,DC=DomainDnsZones,$dom"    # DNS application partition (domain)
    "CN=Infrastructure,DC=ForestDnsZones,$fr"     # DNS application partition (forest)
)
foreach ($o in $objs) {
    try {
        Get-ADObject -Identity $o -Server $dc -Properties fSMORoleOwner -ErrorAction Stop |
            Select-Object DistinguishedName, fSMORoleOwner
    } catch { "Not found on ${dc}: $o" }
}
</code></pre>
<p>Run it with <code>$dc</code> set to each DC you care about. Read the results like this:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Every DC names the same live DC for every role</td>
<td>Ownership is consistent; the failure is about reaching or rights (Steps 3 and 5)</td>
</tr>
<tr>
<td>DCs disagree about an owner</td>
<td>Replication hasn't converged, or one DC is isolated; don't move anything until Step 3 explains why</td>
</tr>
<tr>
<td>An owner value contains <code>DEL:</code> or <code>0ADEL</code></td>
<td>The owner's NTDS Settings object was deleted. Microsoft's RID seize article shows "CN=NTDS Settings DEL:" as an invalid value; its demotion article explains that "The fSMORoleOwner attribute contains the string 0ADEL indicating that the role owning DC's NTDS Settings object has been deleted." (Steps 8 and 9)</td>
</tr>
<tr>
<td>The DNS application partition owners point at an old DC while the five main roles look fine</td>
<td>The classic leftover after a forced removal; <code>netdom query fsmo</code> doesn't show these (Step 9)</td>
</tr>
<tr>
<td>An owner is a DC you've never heard of</td>
<td>Check <code>Get-ADDomainController -Filter *</code> and the Sites and Services Servers container before you assume it's gone</td>
</tr>
<tr>
<td><code>Get-ADObject</code> returns nothing for a DNS partition</td>
<td>That partition may not exist in this domain or forest, or the DC you asked doesn't host it (our reading)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Is the current holder online and replicating? (read-only)</h2>
<p>A transfer only works when the current holder takes part, and a holder only acts as one after it has replicated. Microsoft's transfer-or-seize article: "When a DC that has been acting as a role holder starts to run (for example, after a failure or a shutdown), it doesn't immediately resume behaving as the role holder." The per-role details are in Microsoft's FSMO roles article: the RID master "is active only when the role owner has inbound replicated the domain NC successfully since the Directory Service started", and the schema and domain naming masters have the same rule for their partitions. The PDC emulator is the exception: "There is no inbound replication requirement."</p>
<p>So read replication for the holder and the target before anything else:</p>
<pre><code class="language-console">REM Forest-wide summary: which DCs are failing inbound or outbound replication, errors first
repadmin /replsummary /errorsonly

REM Inbound replication status, per partition and partner, for the target and for the holder
repadmin /showrepl DC02
repadmin /showrepl DC01
</code></pre>
<pre><code class="language-powershell"># Read-only: the target's inbound partners for every partition it holds, with the last result
Get-ADReplicationPartnerMetadata -Target DC02 -Partition * |
    Select-Object Server, Partner, Partition, LastReplicationAttempt, LastReplicationSuccess,
        LastReplicationResult, ConsecutiveReplicationFailures |
    Format-Table -AutoSize

# Read-only: replication failures recorded for the target and the holder
Get-ADReplicationFailure -Target DC02, DC01

# Is the holder's name resolving and answering at all? (ICMP can be blocked; this is a hint, not proof)
Test-NetConnection -ComputerName DC01
</code></pre>
<p>Microsoft's note on the <code>/showrepl</code> output: "If @ [Never] appears in the output for a directory partition, replication of that directory partition has never succeeded from the identified source replication partner over the listed connection." Then look at the events around the failed attempt on the DC that did the work:</p>
<pre><code class="language-powershell"># Directory Service events Microsoft's role articles mention (1586 after a PDC seizure, 2091 in a demotion log)
Get-WinEvent -FilterHashtable @{
    LogName   = 'Directory Service'
    Id        = 1586, 2091
    StartTime = (Get-Date).AddDays(-2)
} -ErrorAction SilentlyContinue | Format-List TimeCreated, Id, Message

# RID issuance events (Microsoft: logged in the System log under Directory-Services-SAM)
Get-WinEvent -FilterHashtable @{
    LogName   = 'System'
    Id        = 16642, 16643, 16644, 16645, 16655, 16656, 16657
    StartTime = (Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue | Format-List TimeCreated, Id, Message
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>The holder is up and replicates both ways with the target</td>
<td>Reachability isn't the problem; check the target (Step 4) and rights (Step 5)</td>
</tr>
<tr>
<td>The holder restarted recently and its inbound replication for the role's partition has failed since</td>
<td>It won't act as the holder yet, and a transfer can fail with 0x20AF. Fix its replication first (Step 6 "wait")</td>
</tr>
<tr>
<td>Error 1722 (RPC server unavailable) between holder and target</td>
<td>A name, port, or firewall problem; <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> covers that chain</td>
</tr>
<tr>
<td>A target-principal-name error between holder and target</td>
<td>Kerberos reached the wrong DC name or a stale password; <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> covers it</td>
</tr>
<tr>
<td>The holder is down or unreachable from every DC</td>
<td>Transfer is impossible until it's back; this is the Step 6 decision</td>
</tr>
<tr>
<td>The target itself fails inbound replication</td>
<td>It isn't "the most recent replicated writable copy"; fix it or pick another target (Step 4)</td>
</tr>
<tr>
<td>RID events on the DC that can't create accounts</td>
<td>The DC's RID pool ran low and the RID master couldn't supply a new one; that's the role you need first</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Is the target DC the right one? (read-only)</h2>
<p>Microsoft's criteria for the new role holder are short: "It resides in the same domain as the previous role holder." and "It has the most recent replicated writable copy of the role partition." Its placement guidance adds: "Place the RID master on the domain PDC in the same domain." and "Place the schema master on the PDC of the forest root domain." The usual answer is a healthy DC in the same site that's a direct replication partner of the old holder. Microsoft's standby-master guidance says to "Ensure that the standby operations masters are direct replication partners of the actual operations masters."</p>
<pre><code class="language-powershell"># Read-only: every DC in the domain, its site, and whether it's a GC or read-only
Get-ADDomainController -Filter * |
    Select-Object Name, Site, IsGlobalCatalog, IsReadOnly |
    Sort-Object Site, Name | Format-Table -AutoSize

# Microsoft's single-DC global catalog check (True or False)
(Get-ADDomainController -Filter { Name -Eq 'DC02' }).IsGlobalCatalog

# Is the AD Recycle Bin enabled? (changes whether the infrastructure master matters)
Get-ADOptionalFeature -Identity 'Recycle Bin Feature'
</code></pre>
<pre><code class="language-console">REM Health of the proposed target, verbose
dcdiag /s:DC02 /v
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>The target is a read-only DC</td>
<td>It can't hold a role. Microsoft: "RODCs cannot act as operations master role holders." and the move cmdlet "does not work with a read-only domain controller."</td>
</tr>
<tr>
<td>The target is in another domain</td>
<td>Domain roles stay in their own domain; pick a DC in the same domain as the old holder</td>
</tr>
<tr>
<td>The target fails <code>dcdiag</code> or replication</td>
<td>Fix it first, or pick a healthier DC; a new holder waits for inbound replication before it acts (Step 10)</td>
</tr>
<tr>
<td>Infrastructure master target is a global catalog</td>
<td>Our reading: matters only when some DCs in a multi-domain forest aren't GCs and the Recycle Bin is off. Microsoft: "Don't put the infrastructure master role on the same DC as the global catalog server." but also "The infrastructure master role isn't used anymore once you enable Active Directory Recycle Bin." and "In a forest that contains a single Active Directory domain, there are no phantoms."</td>
</tr>
<tr>
<td>The target is a far site behind a slow link</td>
<td>Every DC needs to reach the RID master and PDC; prefer a hub-site DC (Microsoft's placement guidance)</td>
</tr>
</tbody></table>
<p>If you need a property that isn't in that list, Microsoft's note on <code>Get-ADDomainController</code> is to pipe one DC to <code>Get-Member</code> and read all of them.</p>
<hr />
<h2>Step 5: Does your account have the rights for this role? (read-only)</h2>
<p>Microsoft's current article: "The signed-in user should be a member of the Enterprise Administrators group to transfer Schema master or Domain naming master roles, or a member of the Domain Administrators group of the domain where the PDC emulator, RID master and the infrastructure master roles are being transferred." Its older per-role procedures name Schema Admins for the schema master ("Membership in Schema Admins, or equivalent, is the minimum required to complete this procedure."), so for a schema master move, be in both (our reading).</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>Group Microsoft names</th>
<th>Where the group lives</th>
</tr>
</thead>
<tbody><tr>
<td>Schema master</td>
<td>Enterprise Admins (current article); Schema Admins (per-role procedure and seize table)</td>
<td>Forest root domain</td>
</tr>
<tr>
<td>Domain naming master</td>
<td>Enterprise Admins</td>
<td>Forest root domain</td>
</tr>
<tr>
<td>PDC emulator, RID master, infrastructure master</td>
<td>Domain Admins of that domain</td>
<td>The domain that holds the role</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only: what the current session's token actually contains (look for the groups above)
whoami /groups

# Read-only: who's in the forest-level groups (ask a forest root DC)
Get-ADGroupMember -Identity 'Schema Admins' -Server DC02 | Select-Object Name, SamAccountName
Get-ADGroupMember -Identity 'Enterprise Admins' -Server DC02 | Select-Object Name, SamAccountName
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>The group is missing from <code>whoami /groups</code> but the account is a member</td>
<td>The membership was added after you signed in; sign out and back in so the token has it</td>
</tr>
<tr>
<td>The groups are there, but ntdsutil says "Access is denied"</td>
<td>The prompt isn't elevated. Microsoft's procedures start with "Open a Command Prompt as an administrator"</td>
</tr>
<tr>
<td>PowerShell stops with a permission error</td>
<td>Microsoft: "If the acting credentials do not have directory-level permission to perform the task, Active Directory module for Windows PowerShell returns a terminating error." Run as the right account, or pass <code>-Credential</code></td>
</tr>
<tr>
<td>Running from a workstation</td>
<td>Fine for PowerShell; for ntdsutil Microsoft says "We recommend that you log on to the DC to which you are assigning Operation Master roles."</td>
</tr>
<tr>
<td>"Access is denied" during metadata cleanup, not the move</td>
<td>A different cause: objects protected from accidental deletion (Step 9)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Transfer, wait, or seize? (decision, no changes yet)</h2>
<p>This is the step the forum post skips. Microsoft's rules:</p>
<ul>
<li>Transfer when "The current role holder is operational and can be accessed on the network by the new Operation Master owner."</li>
<li>Seize when "The current role holder is experiencing an operational error that prevents an Operation Master-dependent operation from completing successfully, and you can't transfer the role.", after a forced demotion, or when "The operating system on the computer that originally owned a specific role no longer exists or has been reinstalled."</li>
<li>Wait when you can: "If the role holder can be made operational before the role is needed, you may delay seizing the role." Except for one role: "The PDC role in each domain should always be online."</li>
</ul>
<p>How much each role can wait, and how bad a duplicate would be (Microsoft's function list and its replication-island table):</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>When you need it (Microsoft)</th>
<th>Conflict if two DCs hold it (Microsoft)</th>
</tr>
</thead>
<tbody><tr>
<td>PDC emulator</td>
<td>"Must be online and accessible 24 hours a day, seven days a week."</td>
<td>No</td>
</tr>
<tr>
<td>RID master</td>
<td>When DCs need a new RID pool, including newly promoted DCs</td>
<td>Yes</td>
</tr>
<tr>
<td>Infrastructure master</td>
<td>Cross-domain references; no work in a single-domain forest</td>
<td>No</td>
</tr>
<tr>
<td>Domain naming master</td>
<td>"Must be online when domains and application partitions in a forest are added or removed."</td>
<td>Yes</td>
</tr>
<tr>
<td>Schema master</td>
<td>"Must be online when schema updates are performed."</td>
<td>Yes</td>
</tr>
</tbody></table>
<p>Then decide:</p>
<table>
<thead>
<tr>
<th>Situation</th>
<th>Decision (our reading of Microsoft's rules)</th>
</tr>
</thead>
<tbody><tr>
<td>Holder online, replicating, transfer failed for a reason Steps 3-5 found</td>
<td>Fix that reason and transfer (Step 7)</td>
</tr>
<tr>
<td>Holder down, returning soon, nothing failing but the PDC functions</td>
<td>Bring it back if you can. If the PDC can't wait, seizing only the PDC is the lowest-risk seizure in Microsoft's island table, but plan the old DC's return as a rebuild or a cleanup (Step 9), not a power-on</td>
</tr>
<tr>
<td>Holder down, returning soon, nothing role-dependent failing</td>
<td>Wait and fix the holder; don't seize</td>
</tr>
<tr>
<td>Holder gone: dead hardware, reinstalled, force-demoted</td>
<td>Seize all its roles on the chosen target (Step 8), then clean up (Step 9)</td>
</tr>
<tr>
<td>The plan is to restore the old DC from a backup</td>
<td>Don't. Microsoft: "The restoration process rebuilds the DC as a role holder again." Rebuild it from scratch instead</td>
</tr>
<tr>
<td>Two parts of the network each have a holder (replication islands)</td>
<td>Microsoft: "Before you reintegrate islands, determine which role holders to keep." Remove the duplicate schema, naming, and RID masters with the cleanup procedures</td>
</tr>
</tbody></table>
<p>Before a seizure, Microsoft also asks for patience: "do not perform a role seizure until enough time has passed to complete at least one end-to-end replication cycle across your network." The more of the old holder's last changes reach the target, the less you lose.</p>
<hr />
<h2>Step 7: Transfer it properly (a change)</h2>
<p>Only when Step 6 says transfer. In PowerShell, <code>-Identity</code> is the DC that gets the roles; Microsoft: "The Identity parameter specifies the directory server that receives the roles." The cmdlet doesn't have to run on either DC: it "can be remotely executed from any domain joined computer where the Active Directory module for Windows PowerShell administration module is installed and available for use."</p>
<pre><code class="language-powershell"># Preview, then move the three domain roles to DC02 (Domain Admins of that domain)
Move-ADDirectoryServerOperationMasterRole -Identity DC02 -OperationMasterRole PDCEmulator, RIDMaster, InfrastructureMaster -WhatIf
Move-ADDirectoryServerOperationMasterRole -Identity DC02 -OperationMasterRole PDCEmulator, RIDMaster, InfrastructureMaster

# The two forest roles (Enterprise Admins; Schema Admins as well for the schema master)
Move-ADDirectoryServerOperationMasterRole -Identity DC02 -OperationMasterRole SchemaMaster, DomainNamingMaster

# Only have the FQDN? Microsoft's Example 5: resolve it with Get-ADDomainController first
$Server = Get-ADDomainController -Identity "dc02.contoso.com"
Move-ADDirectoryServerOperationMasterRole -Identity $Server -OperationMasterRole PDCEmulator
</code></pre>
<p>That last pattern matters: "There is a known issue where the Move-ADDirectoryServerOperationMasterRole cmdlet fails when an FQDN is specified directly as the value of the Identity parameter."</p>
<p>With ntdsutil, run it on the DC that will receive the roles (Microsoft's recommendation), connect to that DC, then transfer. Microsoft's syntax is <code>transfer &lt;role&gt;</code>; type <code>?</code> at the <code>fsmo maintenance:</code> prompt to see the exact role names your version accepts:</p>
<pre><code class="language-console">C:\&gt; ntdsutil
ntdsutil: roles
fsmo maintenance: connections
server connections: connect to server DC02
server connections: q
fsmo maintenance: ?
fsmo maintenance: transfer pdc
fsmo maintenance: transfer rid master
fsmo maintenance: transfer infrastructure master
fsmo maintenance: q
ntdsutil: q
</code></pre>
<p>The consoles do the same thing, one role at a time, as long as you point them at the target DC first:</p>
<table>
<thead>
<tr>
<th>Role</th>
<th>Console</th>
<th>Path (Microsoft's procedures)</th>
</tr>
</thead>
<tbody><tr>
<td>RID master, PDC emulator, infrastructure master</td>
<td>Active Directory Users and Computers</td>
<td>Right-click <strong>Active Directory Users and Computers</strong> &gt; <strong>Change Active Directory Domain Controller</strong> &gt; select DC02 &gt; right-click again &gt; <strong>All Tasks</strong> &gt; <strong>Operations Masters</strong> &gt; the <strong>RID</strong>, <strong>PDC</strong>, or <strong>Infrastructure</strong> tab &gt; <strong>Change</strong></td>
</tr>
<tr>
<td>Domain naming master</td>
<td>Active Directory Domains and Trusts</td>
<td>Right-click <strong>Active Directory Domains and Trusts</strong> &gt; <strong>Change Active Directory Domain Controller</strong> &gt; select DC02 &gt; right-click again &gt; <strong>Operations Master</strong> &gt; <strong>Change</strong></td>
</tr>
<tr>
<td>Schema master</td>
<td>Active Directory Schema snap-in</td>
<td>Register it first with <code>regsvr32 schmmgmt.dll</code> and add it to an MMC; right-click <strong>Active Directory Schema</strong> &gt; <strong>Change Active Directory Domain Controller</strong> &gt; select DC02 &gt; right-click again &gt; <strong>Operations Master</strong> &gt; <strong>Change</strong></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>Transfer result</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Completed, and <code>netdom query fsmo</code> on DC02 shows DC02</td>
<td>Go to Step 10 to prove it everywhere</td>
</tr>
<tr>
<td>0x20AF "The current FSMO holder could not be contacted."</td>
<td>Steps 3 and 6: the holder isn't taking part; don't keep retrying</td>
</tr>
<tr>
<td>"Access is denied" or a permission error</td>
<td>Step 5</td>
</tr>
<tr>
<td>Fails only with the FQDN in <code>-Identity</code></td>
<td>Microsoft's known issue; use the <code>Get-ADDomainController</code> pattern above</td>
</tr>
<tr>
<td>The console's lower box shows the wrong DC</td>
<td>You didn't change the console's domain controller to the target first</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: Seize it properly, only if Step 6 says so (a change)</h2>
<p>A seizure is a decision about the old DC, so make it once and write it on the ticket. Microsoft's older procedure explains the risk: "If two domain controllers are performing the same operations master role simultaneously, the severity of the effect from duplicate operations master roles varies, depending on the role that was seized. The effect can range from no visible effect to potential corruption of the Active Directory database."</p>
<p>On the target DC, in an elevated prompt, connect ntdsutil to that DC (Microsoft: "If ServerFQDN does not succeed, use the NetBIOS name of the DC."), then seize. The seize commands are Microsoft's; note the two exceptions: "Exceptions are for the PDC emulator role, whose syntax is seize pdc, and the Domain naming master, whose syntax is seize naming master."</p>
<pre><code class="language-console">C:\&gt; ntdsutil
ntdsutil: roles
fsmo maintenance: connections
server connections: connect to server DC02.contoso.com
server connections: quit
fsmo maintenance: seize pdc
fsmo maintenance: seize rid master
fsmo maintenance: seize infrastructure master
fsmo maintenance: seize naming master
fsmo maintenance: seize schema master
fsmo maintenance: quit
ntdsutil: quit
</code></pre>
<table>
<thead>
<tr>
<th>Command</th>
<th>Credentials (Microsoft's seize table)</th>
</tr>
</thead>
<tbody><tr>
<td><code>seize naming master</code></td>
<td>Enterprise Admins</td>
</tr>
<tr>
<td><code>seize schema master</code></td>
<td>Schema Admins</td>
</tr>
<tr>
<td><code>seize infrastructure master</code></td>
<td>Domain Admins</td>
</tr>
<tr>
<td><code>seize pdc</code></td>
<td>Domain Admins</td>
</tr>
<tr>
<td><code>seize rid master</code></td>
<td>Domain Admins</td>
</tr>
</tbody></table>
<p>Seize only the roles the dead DC held (from Step 2), not every role in the forest. Expect ntdsutil to try a transfer first: "When you use Ntdsutil.exe to seize an operations master role, the tool first attempts a transfer from the current role owner." You'll see a transfer error and then the seizure. For the RID master, Microsoft adds that the target "tries to synchronize with a replication partner before accepting this role", and if it can't, "a dialog box appears asking you whether you want to continue with the operation despite this computer not being able to synchronize with a partner. Click Yes."</p>
<p>The PowerShell equivalent is the Step 7 command with <code>-Force</code>. Microsoft: "This operation still attempts graceful transfer first, then seizes if transfer is not possible."</p>
<pre><code class="language-powershell"># Seize only the roles the dead DC held (Microsoft's Example 4 form, with this post's role list)
Move-ADDirectoryServerOperationMasterRole -Identity DC02 -OperationMasterRole PDCEmulator, RIDMaster, InfrastructureMaster -Force
</code></pre>
<p>Know what a RID master seizure costs before you run it. Microsoft: "Rid Master seizures increment the next available RID in the pool when you seize the RID master role." The amount depends on the tool: "The Move-ADDirectoryServerOperationMasterRole cmdlet increases the next Rid pool by 30,000 from what it finds in Active Directory." and with ntdsutil "it increases the next Rid pool by 10,000." Microsoft's conclusion: "So seize the Rid Master only when you're sure that the current Rid Master can't be brought back into service." A domain has "capacity for roughly one billion security principals", so one seizure is affordable; a habit of seizing isn't (our reading). Microsoft's read of the remaining pool:</p>
<pre><code class="language-console">REM Microsoft's form: the available RID pool for the domain, from the RidManager test
Dcdiag.exe /TEST:RidManager /v | find /i "Available RID Pool for the Domain"
</code></pre>
<table>
<thead>
<tr>
<th>Seize result</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>The role list at the end shows DC02 for each seized role</td>
<td>Seized. Go to Step 9 before anything else</td>
</tr>
<tr>
<td>"Transfer of RID FSMO failed, proceeding with seizure ..." then "Search failed to find any Domain Controllers"</td>
<td>Microsoft's cause: "The fSMORoleOwner attribute of the RID Manager$ object in Active Directory is invalid." Its fix is to set <code>fSMORoleOwner</code> on <code>CN=RID Manager$,CN=System</code> to the new DC's NTDS Settings DN in ADSI Edit, and "Changing the fSMORoleOwner attribute accomplishes the same thing as seizing the role with Ntdsutil." Use it for that error only</td>
</tr>
<tr>
<td>"Access is denied"</td>
<td>Step 5: wrong group (Schema Admins for the schema master) or not elevated</td>
</tr>
<tr>
<td>You seized the infrastructure master</td>
<td>Microsoft's note: "After you seize the infrastructure master role, you may receive an error later if you need to run Adprep /Rodcprep." Check the DNS application partition owners in Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Clean up the old DC and every owner that still points at it (a change)</h2>
<p>After a seizure, the old DC doesn't get its old life back. Microsoft's options for it: format and reinstall, or forcibly demote it, then remove its metadata from another DC; "After you clean up the metadata, you can repromote the computer to a DC, and transfer a role back to it." Restoring it from a backup is the wrong rebuild: "If you plan to use the repaired computer as a DC, we recommend that you rebuild the computer into a DC from scratch instead of restoring the DC from a backup." When the board in the opener arrives, keep DC01 off the network until it's reinstalled or demoted (our reading).</p>
<p><strong>Metadata cleanup.</strong> Microsoft: "Metadata cleanup is a required procedure after a forced removal of Active Directory Domain Services (AD DS)." The GUI way is to delete the DC's computer object from the <strong>Domain Controllers</strong> OU in Active Directory Users and Computers and select "This Domain Controller is permanently offline and can no longer be demoted using the Active Directory Domain Services Installation Wizard (DCPROMO)". If it still holds roles, that dialog moves them to a DC it picks: "You cannot change this domain controller. If you want to move the role to a different domain controller, you must move the role after you complete the server metadata cleanup procedure." The command-line way, connected to a replication partner of the removed DC (Microsoft: "This domain controller must be a replication partner of the removed domain controller."):</p>
<pre><code class="language-console">C:\&gt; ntdsutil
ntdsutil: metadata cleanup
metadata cleanup: connections
server connections: connect to server DC02
server connections: quit
metadata cleanup: remove selected server DC01
metadata cleanup: quit
ntdsutil: quit
</code></pre>
<p>If cleanup says "Access is denied", Microsoft's note is to "make sure that the computer object and the NTDS Settings object for the domain controller are not protected against accidental deletion." Afterward, Microsoft's check is that the DC is gone from the Domain Controllers OU and that its server object in Sites and Services no longer has an NTDS Settings object.</p>
<p><strong>DNS application partition owners.</strong> Rerun the Step 2 read for <code>CN=Infrastructure,DC=DomainDnsZones,...</code> and <code>CN=Infrastructure,DC=ForestDnsZones,...</code>. If either still names a deleted owner, demotions and <code>adprep /rodcprep</code> fail later. Microsoft's resolution: "Use ADSIEDIT.MSC to assign the DN path for the fsMORoleOwner attribute to a live DC that was a direct replication partner of the original FSMO role owner." or run the script in its adprep article, <code>cscript fixfsmo.vbs DC=DomainDnsZones,DC=contoso,DC=com</code> (and again for ForestDnsZones), copied from that article, not retyped.</p>
<p><strong>Time.</strong> If the PDC emulator of the forest root moved, the time source moved with it. Microsoft: "The PDC emulator at the root of the forest becomes authoritative for the enterprise, and should be configured to gather the time from an external source." Microsoft's <code>w32tm /config</code> example for a manual peer list, plus its <code>/reliable:YES</code> option ("This setting is only meaningful on domain controllers."), with your own time servers in place of its example names:</p>
<pre><code class="language-console">REM On the new forest-root PDC emulator (Microsoft's example peer names; use your own)
w32tm /config /manualpeerlist:"ntpserver.contoso.com clock.adatum.com" /syncfromflags:manual /reliable:YES /update

REM On a former PDC that's still a DC (transfer case): follow the domain hierarchy again
w32tm /config /syncfromflags:domhier /update
</code></pre>
<p><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> covers checking that chain afterward. The PDC emulator is also Microsoft's "Default target domain controller for Group Policy updates.", so Group Policy edits now land on the new holder; if they don't show up on clients, <a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> covers that path. Microsoft's placement article also notes that NTDS Replication event 1586 occurs when "the PDC FSMO role for a particular domain has been seized.", so expect it after a PDC seizure.</p>
<hr />
<h2>Step 10: Prove every DC agrees (read-only)</h2>
<p>A new holder doesn't start work the moment the command returns. Microsoft: "After a role transfer or seizure, the new role holder doesn't act immediately." It waits for a successful inbound replication cycle of the role's partition, and "To have full effect, the change must replicate to all domain controllers in the domain for a domain-level role and to all domain controllers in the forest for a forest-level role."</p>
<pre><code class="language-powershell"># Every DC's answer, not just the one you're signed in to
Get-ADDomainController -Filter * | ForEach-Object {
    $n = $_.Name
    $d = Get-ADDomain -Server $n
    $f = Get-ADForest -Server $n
    [pscustomobject]@{
        AskedDC = $n
        PDC     = $d.PDCEmulator
        RID     = $d.RIDMaster
        Infra   = $d.InfrastructureMaster
        Schema  = $f.SchemaMaster
        Naming  = $f.DomainNamingMaster
    }
} | Format-Table -AutoSize
</code></pre>
<pre><code class="language-console">REM The same check, the dcdiag way, plus RID master health and replication
dcdiag /test:KnowsOfRoleHolders /e
dcdiag /test:RidManager /v
repadmin /replsummary
</code></pre>
<p>Microsoft's ntdsutil view of the connected DC's roles is <strong>roles</strong> &gt; <strong>connections</strong> &gt; <strong>connect to server</strong> DC02 &gt; <strong>quit</strong> &gt; <strong>select operation target</strong> &gt; <strong>list roles for connected server</strong>.</p>
<p>Pass criteria (our reading): every DC names the same live holder for every role, the Step 2 <code>fSMORoleOwner</code> read (including both DNS application partitions) shows no deleted owner, <code>repadmin /replsummary</code> shows no failures, <code>dcdiag /test:RidManager</code> passes, and the operation from the ticket (creating an account on the DC that couldn't, a password reset, a Group Policy edit) works. Then close the ticket with the decision from Step 6 written down.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>Tempting move</th>
<th>Why it's premature (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>Seize all five roles right away</td>
<td>The dead DC may not hold all five, the RID seizure burns RIDs, and nothing may need the forest roles this week</td>
</tr>
<tr>
<td>Power the old DC back on after seizing its roles</td>
<td>Microsoft: "Do not allow a former operations master role holder whose role has been seized to return to an online domain controller." Rebuild it instead</td>
</tr>
<tr>
<td>Restore the old DC from backup</td>
<td>Restores it as a role holder again; Microsoft recommends a rebuild from scratch</td>
</tr>
<tr>
<td>Connect ntdsutil to the dead DC</td>
<td>The connection is to the DC that will receive the role</td>
</tr>
<tr>
<td>Retry the transfer over and over, or reboot the holder</td>
<td>A freshly started holder waits for inbound replication before it acts; fix replication (Step 3)</td>
</tr>
<tr>
<td>Trust <code>netdom query fsmo</code> from one DC</td>
<td>It shows that DC's view; compare several DCs and the raw <code>fSMORoleOwner</code> (Step 2)</td>
</tr>
<tr>
<td>Use <code>dcdiag /test:FsmoCheck</code> as proof</td>
<td>Microsoft: "This test does not test any of the servers for operations master roles."</td>
</tr>
<tr>
<td>Edit <code>fSMORoleOwner</code> in ADSI Edit first</td>
<td>That's a seizure by another route; Microsoft uses it for specific invalid-owner errors, not as a shortcut</td>
</tr>
<tr>
<td>Move the infrastructure master onto a GC without checking</td>
<td>Fine in a single-domain forest, with every DC a GC, or with the Recycle Bin on; otherwise it stops working</td>
</tr>
<tr>
<td>Add yourself to Schema Admins and leave it</td>
<td>Microsoft: "You should carefully manage and monitor membership in the SA group"; add for the change, remove after</td>
</tr>
<tr>
<td>Pass the target's FQDN straight to <code>-Identity</code></td>
<td>Microsoft's known issue; resolve it with <code>Get-ADDomainController</code> first</td>
</tr>
</tbody></table>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Holder online, transfer failed on replication</td>
<td>Fix replication, then transfer</td>
<td>Seizing a role from a DC that's working</td>
</tr>
<tr>
<td>Holder online, transfer failed on rights</td>
<td>The right group in a fresh, elevated session, then transfer</td>
<td>Permanent extra Schema Admins or Enterprise Admins members</td>
</tr>
<tr>
<td>Holder down for days, PDC functions failing</td>
<td>Bring the holder back; if you can't, seize the PDC alone and plan the old DC's rebuild</td>
<td>Seizing all five and powering the old DC on later</td>
</tr>
<tr>
<td>Holder dead or reinstalled</td>
<td>Seize the roles it held on a healthy, up-to-date DC in the same domain and site</td>
<td>Seizing to an RODC, a far site, or a DC that fails replication</td>
</tr>
<tr>
<td>RID master gone</td>
<td>Seize once, with the cost understood</td>
<td>Repeated RID seizures "to be safe"</td>
</tr>
<tr>
<td>Old DC still listed after a seizure</td>
<td>Metadata cleanup from a replication partner</td>
<td>Leaving its NTDS Settings object in place</td>
</tr>
<tr>
<td>DNS application partition owner points at a deleted DC</td>
<td>Microsoft's ADSI Edit or script fix for that partition</td>
<td>Ignoring it until the next demotion fails</td>
</tr>
<tr>
<td>Forest-root PDC moved</td>
<td>Configure the new PDC's external time source</td>
<td>Leaving the old time configuration on a DC that isn't the PDC</td>
</tr>
<tr>
<td>Old hardware repaired</td>
<td>Reinstall, promote fresh, and transfer roles back if you want</td>
<td>Restoring the old DC from backup</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What's the difference between transferring and seizing FSMO roles?</strong>
A transfer moves a role with the current holder's cooperation, so nothing is lost; Microsoft recommends it when "The current role holder is operational and can be accessed on the network by the new Operation Master owner." A seizure assigns the role without the old holder, so it's for a holder that isn't coming back, and that old DC must not return as it was (Steps 6-8).</p>
<p><strong>Why does FSMO role transfer fail with "The current FSMO holder could not be contacted"?</strong>
The DC doing the work couldn't get the current holder to take part: it's offline, unreachable from that DC, or it restarted and hasn't completed inbound replication for the role's partition yet. Read replication for the holder and the target with <code>repadmin /replsummary</code> and <code>repadmin /showrepl</code> before you think about seizing (Steps 3 and 6).</p>
<p><strong>Can I bring the old domain controller back after seizing its roles?</strong>
Not as it was. Microsoft: "Do not allow a former operations master role holder whose role has been seized to return to an online domain controller." Reinstall it or force-demote it, clean up its metadata, then promote it fresh if you want it back as a DC (Step 9).</p>
<p><strong>Why does seizing FSMO roles say "Access is denied"?</strong>
Usually the account isn't in the group the role needs (Schema Admins for the schema master, Enterprise Admins for the domain naming master, Domain Admins for the domain roles), the membership was added after you signed in, or the prompt isn't elevated. Check <code>whoami /groups</code> in the same window (Step 5). During metadata cleanup, it can also mean the objects are protected from accidental deletion (Step 9).</p>
<p><strong>How do I seize FSMO roles with PowerShell?</strong>
Use <code>Move-ADDirectoryServerOperationMasterRole</code> with <code>-Identity</code> set to the DC that receives the roles, <code>-OperationMasterRole</code> listing only the roles the dead DC held, and <code>-Force</code>. It "still attempts graceful transfer first, then seizes if transfer is not possible." For the RID master, it raises the next RID pool by 30,000, three times what ntdsutil does (Step 8).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (when the role holder can't be reached because replication fails with RPC errors)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (when the new forest-root PDC emulator has to become the time source)</li>
<li><a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> (when the holder and the target fail Kerberos between them)</li>
<li><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> (when policy edits stall after the PDC emulator moves)</li>
</ul>
<h2>Free checklist</h2>
<p>Seizing all five roles before anyone read who owns them, or whether the old DC is coming back, is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=fsmo-role-transfer-failed">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=fsmo-role-transfer-failed</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=fsmo-role-transfer-failed">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=fsmo-role-transfer-failed</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-6 and 10 are read-only checks: <code>netdom query</code>, <code>Get-ADDomain</code>, <code>Get-ADForest</code>, <code>Get-ADRootDSE</code>, <code>Get-ADObject</code>, <code>dcdiag</code>, <code>repadmin /replsummary</code> and <code>/showrepl</code>, <code>Get-ADReplicationPartnerMetadata</code>, <code>Get-ADReplicationFailure</code>, <code>Test-NetConnection</code>, <code>Get-WinEvent</code>, <code>Get-ADDomainController</code>, <code>Get-ADOptionalFeature</code>, <code>whoami /groups</code>, and <code>Get-ADGroupMember</code>. Transferring or seizing roles, editing <code>fSMORoleOwner</code>, metadata cleanup, demoting or rebuilding a DC, changing group membership, and changing the time configuration are deliberate changes. Change one link at a time, prove it from every DC, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Hyper-V Live Migration Failed (Event 21502): A Read-First Switch, Processor, Version, and Kerberos Triage]]></title><description><![CDATA[Hyper-V live migration failed with Event 21502? Read the inner error, Compare-VM, switch names, processor compatibility, VM version, and delegation first.]]></description><link>https://adminpackstudio.hashnode.dev/hyper-v-live-migration-failed-event-21502-a-read-first-switch-processor-version-and-kerberos-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/hyper-v-live-migration-failed-event-21502-a-read-first-switch-processor-version-and-kerberos-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[virtualization]]></category><category><![CDATA[hyper-v]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 08:42:52 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since we added the two new hosts last week, moving VMs to them fails. Hyper-V Manager says the migration operation failed at migration source, and the cluster logged Event 21502. One VM moves fine, three don't, and one we moved last month won't go back to the old host either. A forum post says to turn on processor compatibility for every VM, switch every host to CredSSP, allow live migration on any network, and turn off the firewall on the new hosts. Can we do that?"</p>
<p>Not yet. <strong>"Hyper-V live migration failed" is almost never one setting. Event 21502 is the summary Hyper-V logs when a live migration fails (you'll see it most on clustered hosts), and the reason is in the message text under it: a virtual switch name that doesn't exist on the destination, processor features the destination can't offer, a configuration version it doesn't support, migration settings that don't match, an authentication path that can't delegate, a connection to TCP 6600 that never opens, or a destination that can't hold the VM (memory, storage path, ISO, owners).</strong> Microsoft says it directly: "live migration can fail due to a wide array of factors", and its troubleshooting page lists about twenty different messages under the single heading Event ID 21502. Turning on processor compatibility for every VM hides CPU features from workloads that didn't need it (and needs each VM shut down), switching to CredSSP trades a delegation problem for a sign-in problem (and stops working on Windows Server 2025 with Credential Guard), any-network migration sends unencrypted VM memory over networks you didn't choose, and turning off the host firewall proves nothing about why the listener on 6600 isn't answering. Work through it as a chain: <strong>which VM, which hosts, which inner message</strong> → <strong>what does the full error say on both hosts?</strong> → <strong>what does <code>Compare-VM</code> report?</strong> → <strong>do the virtual switches, adapters, and attached media exist on the destination?</strong> → <strong>can the destination offer the VM's processor features?</strong> → <strong>does the destination support the VM's configuration version?</strong> → <strong>are migration settings and networks the same on both sides?</strong> → <strong>can the source authenticate to the destination?</strong> → <strong>does TCP 6600 connect?</strong> → <strong>can the destination hold the VM?</strong> <code>Get-WinEvent</code> on the Hyper-V logs of both hosts, <code>Compare-VM</code>, <code>Get-VMNetworkAdapter</code>, <code>Get-VMSwitch</code>, <code>Get-VMProcessor</code>, <code>Get-VM</code>, <code>Get-VMHostSupportedVersion</code>, <code>Get-VMHost</code>, <code>Get-VMMigrationNetwork</code>, <code>Get-ADComputer</code>, <code>Test-NetConnection</code>, and the failover cluster cmdlets are the sources for that chain. Change processor compatibility, authentication, and networks at once and you'll never know which one was broken.</p>
<p>If the inner message is "The target principal name is incorrect", <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> covers the Kerberos side of that error. If the VM won't move because a checkpoint or its AVHDX chain is broken, <a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed</a> covers that side. This post covers <strong>"a running Hyper-V VM won't live migrate between Windows Server hosts, clustered or not: Event 21502 in the cluster, 'failed at migration source' or 'failed at migration destination' in Hyper-V Manager, a processor or hardware compatibility error, a failed-to-authenticate or no-credentials error, a failed-to-establish-a-connection error, or a failure that happens one way only, and you need a read-first path before you change processor compatibility, authentication, migration networks, or firewall rules."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Event 21502 is the envelope, not the letter.</strong> Read the inner message and its hex code on both hosts before you change anything (Step 2). Microsoft's first check is a compatibility report: run <code>Compare-VM</code> against the destination (Step 3).</li>
<li><strong>Switch names must match exactly.</strong> Microsoft: "The virtual switch must have the same name on all nodes." (Step 4).</li>
<li><strong>Processor compatibility is per VM, needs the VM off, and has limits.</strong> "Even when processor compatibility is enabled, you can't move VMs between hosts with different processor manufacturers." (Step 5).</li>
<li><strong>One way only usually means version.</strong> A VM upgraded on a newer host can't go back: "This action can't be rolled back." (Step 6).</li>
<li><strong>Authentication is a choice with consequences.</strong> "Kerberos lets you avoid signing in to the server, but requires you to set up constrained delegation." CredSSP needs a sign-in on the source and breaks with Credential Guard on Windows Server 2025 (Step 8).</li>
<li><strong>Read the listener before the firewall.</strong> Microsoft's checklist: "Check whether the TCP Port 6600 and 3343 (for clustering) are listening on both sides." (Step 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which VM, which source and destination, clustered or not, started from where, the full message, since when</td>
<td>"Live migration is broken" with no VM, host pair, or time written down</td>
</tr>
<tr>
<td>2. Full error read</td>
<td>The inner message and hex code are known, on both the source and the destination</td>
<td>Event 21502 quoted alone; the actual reason never read</td>
</tr>
<tr>
<td>3. Compatibility report</td>
<td><code>Compare-VM</code> from the source against the destination returns no incompatibilities</td>
<td>The report names a missing switch, a processor, or a version the destination can't take</td>
</tr>
<tr>
<td>4. Switches and media</td>
<td>Every switch the VM uses exists with the same name on the destination; attached ISOs are reachable there</td>
<td>Fails at less than 10%; a switch that "doesn't exist on the destination node"; an ISO or virtual disk provider error</td>
</tr>
<tr>
<td>5. Processor</td>
<td>The destination offers the processor features the running VM was started with</td>
<td>"processor-specific features not supported"; "hardware on the destination computer isn't compatible"</td>
</tr>
<tr>
<td>6. Configuration version</td>
<td>The destination supports the VM's configuration version</td>
<td>Moves one way, fails the other; Event 10698 with 21502</td>
</tr>
<tr>
<td>7. Settings and networks</td>
<td>Migration enabled, same authentication protocol, a reachable migration network on both sides, limits not hit</td>
<td>Event 21501 (limit reached); traffic on the wrong network; 0x000013AB in a cluster</td>
</tr>
<tr>
<td>8. Authentication</td>
<td>CredSSP with a sign-in on the source, or Kerberos with constrained delegation both ways</td>
<td>0x8009030E "No credentials are available"; 0x8009030D; 0x80090322</td>
</tr>
<tr>
<td>9. Connection</td>
<td>The destination's listener on TCP 6600 answers the source (and 3343 between cluster nodes)</td>
<td>0x8007274D refused; 0x8007274C timed out; 0x8007271D access forbidden; listener disabled</td>
</tr>
<tr>
<td>10. Destination capacity</td>
<td>Enough memory, a writable storage path, the right possible owners, no leftover state</td>
<td>0x800705AA or 0x8007000E memory; Event 20413 path; 0x80071398 owner; folder errors</td>
</tr>
<tr>
<td>11. Change</td>
<td>Only after Steps 1-10 agree: one link, one change</td>
<td>Every VM in compatibility mode, every host on CredSSP, same failure</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which VM, and how many</strong></td>
<td>One VM, every VM to one host, or every VM everywhere. One VM points at its own settings (Steps 4-6); one destination points at that host (Steps 4, 7, 9, 10)</td>
</tr>
<tr>
<td><strong>Source and destination host</strong></td>
<td>Exact names, Windows Server version and build of each, CPU vendor and generation of each. Different versions point at Step 6; different CPUs at Step 5</td>
</tr>
<tr>
<td><strong>Clustered or not</strong></td>
<td>A clustered VM moved in Failover Cluster Manager, a standalone move in Hyper-V Manager, a <code>Move-VM</code>, or System Center Virtual Machine Manager. The settings that apply differ (Step 7)</td>
</tr>
<tr>
<td><strong>Started from where</strong></td>
<td>Signed in on the source host, on the destination, or from a management server or jump box. With CredSSP it matters a lot (Step 8)</td>
</tr>
<tr>
<td><strong>Full message and percentage</strong></td>
<td>The inner lines, the hex code, and roughly how far it got. Microsoft ties "fails at &lt; 10%" to a missing switch and "90%~100%" to a path problem (Steps 4 and 10)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>New hosts, a Windows Server upgrade, BIOS or firmware updates, a VM configuration upgrade, a new switch, a GPO or security baseline, an AD delegation change</td>
</tr>
<tr>
<td><strong>Time of one failing attempt</strong></td>
<td>So you can match it to events on both hosts and in the cluster log (Step 2)</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>a virtual switch that "doesn't exist on the destination node", or failure under 10%</td>
<td>The switch name differs on the destination (Microsoft's 21502 entry)</td>
<td>Step 4</td>
</tr>
<tr>
<td>"processor-specific features not supported" or "hardware on the destination computer isn't compatible"</td>
<td>CPU features, BIOS settings, or microcode differ between hosts</td>
<td>Step 5</td>
</tr>
<tr>
<td>Works one way, fails coming back</td>
<td>The VM's configuration version was upgraded on the newer host</td>
<td>Step 6</td>
</tr>
<tr>
<td>"please wait for completion of an ongoing migration operation" (Event 21501)</td>
<td>Too many simultaneous migrations for the current limit</td>
<td>Step 7</td>
</tr>
<tr>
<td>0x8009030E "No credentials are available in the security package"</td>
<td>CredSSP without a sign-in on the source, or Kerberos delegation missing or blocked</td>
<td>Step 8</td>
</tr>
<tr>
<td>0x80090322 "The target principal name is incorrect"</td>
<td>Kerberos reached the wrong host or name; Microsoft's example is a cross-cluster move on the wrong network</td>
<td>Steps 7 and 8</td>
</tr>
<tr>
<td>0x8007274D, 0x8007274C, 0x80072746, or 0x8007271D</td>
<td>The connection to TCP 6600: refused, timed out, reset, or blocked</td>
<td>Step 9</td>
</tr>
<tr>
<td>"disabled the listener for Virtual Machine migration connections"</td>
<td>Group Policy processing on the destination failed</td>
<td>Step 9</td>
</tr>
<tr>
<td>0x800705AA, 0x8007000E, 0x80071398, Event 20413, or a folder error</td>
<td>The destination can't hold the VM, can't own it, or can't reach its path</td>
<td>Step 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the full error on both hosts (read-only)</h2>
<p>Event 21502 is a summary. The reason is in the lines under it, and often in a second event on the other host. Microsoft's own list for Event ID 21502 runs from a missing virtual switch to processor features, Kerberos errors, memory, ISO files, and storage oplocks, and in Failover Cluster Manager it points you to the VM's <strong>Information details</strong> &gt; <strong>Show details</strong> for the full text.</p>
<p>Find the Hyper-V and clustering logs that have events on each host, then pull the migration events around the failed attempt from both the source and the destination:</p>
<pre><code class="language-powershell"># On the source and on the destination: which Hyper-V and cluster logs have events here
Get-WinEvent -ListLog '*Hyper-V*', '*FailoverClustering*' -ErrorAction SilentlyContinue |
    Where-Object { $_.RecordCount -gt 0 } |
    Sort-Object LogName |
    Format-Table LogName, RecordCount -AutoSize

# The migration events from the last hour, oldest first, with the full message text
$logs = (Get-WinEvent -ListLog '*Hyper-V*' -ErrorAction SilentlyContinue |
    Where-Object { $_.RecordCount -gt 0 }).LogName
Get-WinEvent -FilterHashtable @{
    LogName   = $logs
    Id        = 21502, 21501, 21125, 21024, 20413, 20417, 10698
    StartTime = (Get-Date).AddHours(-1)
} -ErrorAction SilentlyContinue |
    Sort-Object TimeCreated |
    Format-List TimeCreated, MachineName, LogName, Id, Message
</code></pre>
<p>The event IDs in that filter are the ones Microsoft's live migration troubleshooting pages describe: 21502 (the failure itself), 21501 (migration limit), 21125 (configuration setup failed on the destination), 21024 (lost network after migration), 20413 (configuration registration failed on the destination), 20417 (long blackout time), and 10698 (listed with 21502 for configuration version problems). Widen the time window or drop the <code>Id</code> filter if nothing comes back; the inner error is often logged under a different ID on the other host. Microsoft's data collection list also names the worker log ("Microsoft-Windows-Hyper-V-Worker-Admin") and, for clusters, <code>Get-ClusterLog -UseLocalTime -Destination &lt;Path&gt;</code>:</p>
<pre><code class="language-powershell"># Clustered hosts only: write the cluster log for every node, in local time, to one folder
Get-ClusterLog -UseLocalTime -Destination C:\Temp\ClusterLogs
</code></pre>
<p>Read the hex code first. Microsoft's 21502 entries map them like this:</p>
<table>
<thead>
<tr>
<th>Code or text in the message</th>
<th>Microsoft's meaning</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>"a virtual switch used by the VM doesn't exist on the destination node"</td>
<td>Switch name missing or different on the destination</td>
<td>Step 4</td>
</tr>
<tr>
<td>0xC03A0014 "A virtual disk support provider for the specified file was not found."</td>
<td>An attached ISO the destination can't open</td>
<td>Step 4</td>
</tr>
<tr>
<td>"processor-specific features not supported on host"</td>
<td>CPU features differ</td>
<td>Step 5</td>
</tr>
<tr>
<td>"The hardware on the destination computer isn't compatible with the hardware requirements of this virtual machine"</td>
<td>Processor compatibility off, or microcode differs</td>
<td>Step 5</td>
</tr>
<tr>
<td>"please wait for completion of an ongoing migration operation" (Event 21501)</td>
<td>Simultaneous migration limit</td>
<td>Step 7</td>
</tr>
<tr>
<td>0x000013AB "A cluster Network isn't available for this operation."</td>
<td>No usable cluster network for the move</td>
<td>Step 7</td>
</tr>
<tr>
<td>0x8009030E "No credentials are available in the security package"</td>
<td>No credentials to delegate</td>
<td>Step 8</td>
</tr>
<tr>
<td>0x8009030D "The credentials supplied to the package were not recognized"</td>
<td>Source can't authenticate to the destination</td>
<td>Step 8</td>
</tr>
<tr>
<td>0x80090322 "The target principal name is incorrect."</td>
<td>Wrong host or network for Kerberos; cross-cluster network order</td>
<td>Steps 7 and 8</td>
</tr>
<tr>
<td>0x8007274D, 0x80072746, 0x8007274C</td>
<td>Connection refused, reset, or timed out</td>
<td>Step 9</td>
</tr>
<tr>
<td>0x8007271D "An attempt was made to access a socket in a way forbidden by its access permissions."</td>
<td>Firewall or antivirus</td>
<td>Step 9</td>
</tr>
<tr>
<td>"The Virtual Machine Management Service disabled the listener for Virtual Machine migration connections."</td>
<td>Group Policy failure on the host</td>
<td>Step 9</td>
</tr>
<tr>
<td>0x800705AA, 0x8007000E</td>
<td>Not enough memory on the destination</td>
<td>Step 10</td>
</tr>
<tr>
<td>0x80071398 "the specified cluster node isn't the owner of the group, or the node isn't a possible owner of the group"</td>
<td>Possible owners don't include the destination</td>
<td>Step 10</td>
</tr>
<tr>
<td>0x80070569 "Logon failure: the user has not been granted the requested logon type at this computer."</td>
<td>User rights for the virtual machine accounts</td>
<td>Step 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: What does Compare-VM report? (read-only)</h2>
<p>Microsoft's basic checklist says to "Check compatibilities issues by running a Compare-VM command." The cmdlet doesn't move anything: "The Compare-VM cmdlet compares a virtual machine and a virtual machine host for compatibility, returning a compatibility report." Run it on the source host, against the destination, for the VM that failed:</p>
<pre><code class="language-powershell"># On the source host: compare the running VM with the destination host
$report = Compare-VM -Name APP01 -DestinationHost HV02

# Each incompatibility has a message, an ID, and the object it came from
$report.Incompatibilities | Format-Table Message, MessageId, Source -AutoSize -Wrap

# If the move includes storage, compare that move instead
$report = Compare-VM -Name APP01 -DestinationHost HV02 -IncludeStorage -DestinationStoragePath D:\VMs\APP01
$report.Incompatibilities | Format-Table Message, MessageId, Source -AutoSize -Wrap
</code></pre>
<p><code>Message</code>, <code>MessageId</code>, and <code>Source</code> are the columns Microsoft's own <code>Compare-VM</code> example shows, where the report says "Could not find Ethernet switch 'Production'." for a VM whose adapter used a switch the host didn't have. An empty <code>Incompatibilities</code> list means the destination can take the VM's configuration, so look at Steps 7-10 (settings, authentication, connection, capacity). A non-empty list usually tells you which of Steps 4-6 to read next (our reading). If <code>Compare-VM</code> itself fails with a credentials or connection error, that's the same problem the migration has: go to Steps 8 and 9.</p>
<hr />
<h2>Step 4: Do the switches, adapters, and attached media exist on the destination? (read-only)</h2>
<p>The first entry under Event ID 21502 on Microsoft's live migration page is a switch: "Live migration of VM failed. VM failed to live migrate because a virtual switch used by the VM doesn't exist on the destination node". The action is short: "The virtual switch must have the same name on all nodes." Microsoft also notes "If a virtual machine is connected to a virtual switch that doesn't exist on the destination node, then live migration fails at &lt; 10%."</p>
<pre><code class="language-powershell"># On the source: which switch each of the VM's adapters uses
Get-VMNetworkAdapter -VMName APP01 | Format-Table VMName, Name, SwitchName -AutoSize

# On both hosts: the switches that exist, by exact name and type
Get-VMSwitch | Sort-Object Name | Format-Table Name, SwitchType -AutoSize
Get-VMSwitch -ComputerName HV02 | Sort-Object Name | Format-Table Name, SwitchType -AutoSize

# On the source: anything attached to the VM's DVD drives
Get-VMDvdDrive -VMName APP01 | Format-Table VMName, ControllerType, ControllerNumber, Path -AutoSize
</code></pre>
<p>What to look for:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>A <code>SwitchName</code> on the source that isn't in the destination's list</td>
<td>The cause Microsoft lists first; names must match exactly</td>
</tr>
<tr>
<td>Same name, different <code>SwitchType</code></td>
<td>The name matches but it's Internal or Private on one side and External on the other; the VM loses its network or the cluster check fails</td>
</tr>
<tr>
<td>Adapter on an Internal or Private switch, VM clustered</td>
<td>Microsoft: "You can't live migrate a VM across nodes in a cluster if that VM is connected to an internal or private virtual switch." The message says "the destination has disconnected VM switch(s)"</td>
</tr>
<tr>
<td>A <code>Path</code> to an ISO on a local disk or a share the destination can't read</td>
<td>Microsoft's 0xC03A0014 entry: an ISO the destination can't open fails the move; the fix is to set the DVD drive to <strong>None</strong></td>
</tr>
<tr>
<td>Event 21125 "Configuration setup for live migration failed on the destination node."</td>
<td>The same switch problem seen from the destination; Microsoft's action is to make the virtual network names match and refresh the VM configuration in Failover Cluster Manager</td>
</tr>
<tr>
<td>Event 21024 after a successful move</td>
<td>The move worked but the VM lost its network: different subnets or VLANs on the destination switch, not a migration failure</td>
</tr>
</tbody></table>
<p>In a cluster, after you rename or create a switch, Microsoft's steps end with Failover Cluster Manager &gt; the VM role &gt; <strong>More Actions</strong> &gt; <strong>Refresh virtual machine configuration</strong> before you retry. That refresh is a change (Step 11), not a check.</p>
<p>Microsoft's broader checklist adds team consistency: "Validate network teaming (SET or LBFO) consistency." A switch with the right name bound to a team with a different configuration is worth a second look (our reading).</p>
<hr />
<h2>Step 5: Can the destination offer the VM's processor features? (read-only)</h2>
<p>A running VM sees the features of the host it started on. Microsoft explains why that blocks a move: "If software in the VM detects and starts using a particular processor capability, and then the VM is moved to a new virtualization host without that capability, the software can fail." So "Hyper-V performs initial checks whenever a VM live migration or save/restore operation is initiated", and "If these feature sets don't match, the migration or restore operation is canceled."</p>
<pre><code class="language-powershell"># On the source: is processor compatibility turned on for this VM, and in which mode
Get-VMProcessor -VMName APP01 | Format-List VMName, Count, CompatibilityFor*

# Same check for every VM on the host
Get-VM | Get-VMProcessor | Format-Table VMName, Count, CompatibilityForMigrationEnabled -AutoSize

# Does the VM have checkpoints (Microsoft ties this to the hardware-not-compatible error)
Get-VMSnapshot -VMName APP01 | Format-Table VMName, Name, SnapshotType -AutoSize
</code></pre>
<p>Read the result against the messages:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td><code>CompatibilityForMigrationEnabled</code> False, hosts with different CPU generations</td>
<td>Expected failure. Microsoft: "If a virtual machine doesn't have processor compatibility turned on and has one or more snapshots, the move fails if the hosts have different processor versions."</td>
</tr>
<tr>
<td>Compatibility True, still "processor-specific features not supported"</td>
<td>Microsoft's checks: "Make sure the BIOS of the host has the same settings." and "Run the Get-SpeculationControlSettings cmdlet and check the results. It should be the same on all nodes."</td>
</tr>
<tr>
<td>Intel source, AMD destination (or the reverse)</td>
<td>No setting fixes it: "Even when processor compatibility is enabled, you can't move VMs between hosts with different processor manufacturers." Shut down and move, or rebuild on the other platform</td>
</tr>
<tr>
<td>New hosts got a microcode or firmware update the old ones didn't</td>
<td>Microsoft's guidance for side channel mitigations: "the best option is to live migrate VMs only from old to new microcode (uCode) revisions"</td>
</tr>
<tr>
<td>Windows Server 2025 cluster, <code>CompatibilityForMigrationMode</code> shown</td>
<td>Dynamic processor compatibility (<code>CommonClusterFeatureSet</code>) needs configuration version 10.0 or later; <code>MinimumFeatureSet</code> is the standard mode Microsoft says to use toward older processors outside the cluster</td>
</tr>
</tbody></table>
<p>Two rules from Microsoft keep this from turning into a fleet-wide change. First, "You don't need to enable processor compatibility mode if you plan to stop and restart the VMs", because the guest reads the new host's features at boot. Second, "Processor compatibility mode can't be enabled or disabled while the VM is running." So for a running VM that has to move now, the choice is a short shutdown either way; the guide's tip is "Always start VMs for the first time on the oldest (least capable) CPU host." The <code>CompatibilityFor*</code> wildcard is our form; it shows <code>CompatibilityForMigrationMode</code> only where the host's Hyper-V module has it.</p>
<hr />
<h2>Step 6: Does the destination support the VM's configuration version? (read-only)</h2>
<p>Microsoft's troubleshooting guide describes the pattern exactly: "Migration works one way but not in reverse (especially after moving to a newer OS)." with Event IDs 10698 and 21502. The VM's configuration version decides which hosts can run it:</p>
<pre><code class="language-powershell"># On the source: configuration version of every VM (Microsoft's form)
Get-VM * | Format-Table Name, Version

# On the destination: which configuration versions it can run, and its default
Get-VMHostSupportedVersion -ComputerName HV02
Get-VMHostSupportedVersion -ComputerName HV02 -Default

# On the source: integration services that report a version mismatch
Get-VMIntegrationService -VMName APP01 |
    Format-Table VMName, Name, SecondaryOperationalStatus -AutoSize
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>The VM's <code>Version</code> isn't in the destination's <code>Get-VMHostSupportedVersion</code> list</td>
<td>The destination is an older Windows Server than the version needs. You can't fix this from the VM side; move it to a host that supports it</td>
</tr>
<tr>
<td>Same version on both lists, moves one way only</td>
<td>Look at Steps 5 and 7-9 instead; the version isn't the blocker (our reading)</td>
</tr>
<tr>
<td><code>SecondaryOperationalStatus</code> ProtocolMismatch</td>
<td>Out-of-date integration services; Microsoft's checklist says to "Confirm VM integration services are up-to-date."</td>
</tr>
</tbody></table>
<p>Don't upgrade a VM's configuration version to make a mixed-version move work. Microsoft's guidance is not to upgrade until "You upgrade your Hyper-V hosts to the latest version of Windows or Windows Server.", "You upgrade the cluster functional level.", and "You're sure that you won't need to move the virtual machine back to a Hyper-V host that runs a previous version of Windows or Windows Server." The troubleshooting guide adds: "This action can't be rolled back. VMs with the upgraded version can't be migrated back to older hosts." During a rolling upgrade, keep VMs at the version the oldest host supports until the last old host is gone (our reading).</p>
<hr />
<h2>Step 7: Are migration settings and networks the same on both sides? (read-only)</h2>
<p>Microsoft's basic checklist has two lines for this: "Check whether the migration is authorized on each side." and "Check whether the protocol used is identical on each side." Read the host settings on both hosts, then the networks each one accepts migrations on:</p>
<pre><code class="language-powershell"># On both hosts: every live migration setting (the wildcard is our form)
Get-VMHost | Format-List Name, *Migration*
Get-VMHost -ComputerName HV02 | Format-List Name, *Migration*

# Networks each host accepts incoming live migration on
Get-VMMigrationNetwork
Get-VMMigrationNetwork -ComputerName HV02

# Clustered hosts: cluster networks and their role, the cluster-wide limit,
# and which networks the Virtual Machine resource type excludes from migration
Get-ClusterNetwork | Format-Table Name, Role, Address -AutoSize
(Get-Cluster).MaximumParallelMigrations
Get-ClusterResourceType -Name "Virtual Machine" | Get-ClusterParameter -Name MigrationExcludeNetworks
</code></pre>
<p>What the settings mean, from Microsoft's <code>Set-VMHost</code> reference:</p>
<table>
<thead>
<tr>
<th>Setting</th>
<th>What to compare</th>
</tr>
</thead>
<tbody><tr>
<td><code>VirtualMachineMigrationAuthenticationType</code></td>
<td>"The acceptable values for this parameter are: Kerberos and CredSSP." Both hosts should use the same one (Step 8)</td>
</tr>
<tr>
<td><code>UseAnyNetworkForMigration</code></td>
<td>"If set to $False, incoming live migration traffic is transmitted only on the networks specified in the MigrationNetworks property of the host." The destination must list a network the source can reach</td>
</tr>
<tr>
<td><code>MaximumVirtualMachineMigrations</code></td>
<td>The per-host limit. In a cluster, the cluster property wins: Microsoft says to check it with <code>(Get-Cluster).MaximumParallelMigrations</code></td>
</tr>
<tr>
<td><code>VirtualMachineMigrationPerformanceOption</code></td>
<td>TCP/IP, Compression, or SMB. "Compression is the default setting." SMB moves memory over SMB 3.0 instead of a plain TCP stream</td>
</tr>
</tbody></table>
<p>Read the result:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Migration not enabled on the destination</td>
<td>Microsoft's setup step is Hyper-V Settings &gt; Live Migrations &gt; <strong>Enable incoming and outgoing live migrations</strong>, on each host</td>
</tr>
<tr>
<td>Kerberos on one host, CredSSP on the other</td>
<td>The source can't authenticate the way the destination expects; pick one (Step 8)</td>
</tr>
<tr>
<td><code>UseAnyNetworkForMigration</code> False and no shared subnet in <code>Get-VMMigrationNetwork</code></td>
<td>The destination only accepts migrations on a network the source isn't on; the connection fails (Step 9)</td>
</tr>
<tr>
<td>Event 21501, "please wait for completion of an ongoing migration operation"</td>
<td>The limit was hit. Microsoft: "Wait to finish other live migrations or increase the number of simultaneous live migrations."</td>
</tr>
<tr>
<td>Cluster, 0x000013AB</td>
<td>Microsoft: "Make sure the cluster network is configured and Allow cluster network communication on this network is selected." A <code>Role</code> of 0 means "Do not allow cluster network communication"</td>
</tr>
<tr>
<td><code>MigrationExcludeNetworks</code> lists the only network both nodes share</td>
<td>The cluster has nothing left to migrate over</td>
</tr>
<tr>
<td>Cross-cluster move, 0x80090322</td>
<td>Microsoft: "Migration was cross-cluster but as per Live Migration Network order, the private Network was selected."</td>
</tr>
<tr>
<td>Event 20417, "unexpectedly long blackout time"</td>
<td>The move worked but slowly. Microsoft: "Make sure there's enough bandwidth available for the live migration."</td>
</tr>
</tbody></table>
<p>Microsoft's cluster network guidance sets the rule for every host involved: "All Hyper-V hosts that can initiate or receive a live migration must have connectivity to a network that is configured to allow live migrations." It also explains why "allow any network" is not a free fix: "live migration traffic isn't encrypted when sent over the network", so isolate it "onto trusted, private networks". In a cluster, "By default, live migration traffic uses the cluster network topology to discover available networks and to establish priority." The order and exclusions you set in Failover Cluster Manager (right-click <strong>Networks</strong> &gt; <strong>Live Migration Settings</strong>) are what <code>MigrationExcludeNetworks</code> shows.</p>
<hr />
<h2>Step 8: Can the source authenticate to the destination? (read-only)</h2>
<p>The source host opens the migration connection to the destination, so it needs credentials it can present there. Microsoft's setup page gives the two options and their cost: "Kerberos lets you avoid signing in to the server, but requires you to set up constrained delegation." and "CredSSP lets you avoid setting up constrained delegation, but requires you to sign in to the source server." When neither works you get this, from the same page:</p>
<pre><code class="language-console">Virtual machine migration operation failed at migration Source.
Failed to establish a connection with host computer name: No credentials are available in the security package 0x8009030E.
</code></pre>
<p>Read which protocol each host uses, what each host may delegate to in AD, and what the account starting the move allows:</p>
<pre><code class="language-powershell"># Which protocol each host uses for live migration
Get-VMHost -ComputerName HV01, HV02 |
    Format-Table Name, VirtualMachineMigrationAuthenticationType -AutoSize

# Kerberos: what each host may delegate to (Microsoft's blog form; run for both hosts)
Get-ADComputer -Identity HV01 -Properties msDS-AllowedToDelegateTo |
    Select-Object -ExpandProperty msDS-AllowedToDelegateTo
Get-ADComputer -Identity HV02 -Properties msDS-AllowedToDelegateTo |
    Select-Object -ExpandProperty msDS-AllowedToDelegateTo

# Protocol transition ("Use any authentication protocol") on each host's account
Get-ADComputer -Identity HV01 -Properties TrustedToAuthForDelegation |
    Format-Table Name, TrustedToAuthForDelegation -AutoSize

# The admin account that starts the move: marked sensitive, or in Protected Users?
Get-ADUser -Identity jsmith-adm -Properties AccountNotDelegated |
    Format-Table Name, AccountNotDelegated -AutoSize
Get-ADGroupMember -Identity "Protected Users" | Format-Table Name, SamAccountName -AutoSize
</code></pre>
<pre><code class="language-console">REM SPNs registered on each host (Microsoft's data collection form)
setspn -L HV01
setspn -L HV02
</code></pre>
<p>Read the result:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>CredSSP, move started from a management server, Failover Cluster Manager on another node, or a remote console</td>
<td>Expected 0x8009030E. Microsoft: "To fix this problem, sign in to the source server and try the move again." Moving back means signing in to the other host first</td>
</tr>
<tr>
<td>Source host is Windows Server 2025, CredSSP</td>
<td>Microsoft: "Starting with Windows Server 2025, Credential Guard is enabled by default on all domain-joined servers that aren't domain controllers." and "If the source machine of a Live Migration uses CredSSP for delegation with Credential Guard enabled, the Live Migration fails."</td>
</tr>
<tr>
<td>Kerberos, but a host's <code>msDS-AllowedToDelegateTo</code> lacks the other host</td>
<td>Delegation is missing, or set one way only. Microsoft's steps set it on the source for the destination and then "repeat the process" on the destination for the source</td>
</tr>
<tr>
<td>The list has Microsoft Virtual Console Service, not Migration Service</td>
<td>Microsoft's blog warns "don't mix up the Microsoft Virtual System Migration Service with the Microsoft Virtual Console Service" in Active Directory Users and Computers</td>
</tr>
<tr>
<td>No cifs entry for the other host</td>
<td>Moves of the VM alone may work; moves that include storage need it: "Add cifs (required to migrate storage) and Microsoft Virtual System Migration Service (required to migrate virtual machine)."</td>
</tr>
<tr>
<td><code>TrustedToAuthForDelegation</code> False ("Use Kerberos only")</td>
<td>Microsoft's blog: on Windows Server 2016 and later, live migration started from a remote system needs "Use any authentication protocol", because it uses protocol transition (paraphrased)</td>
</tr>
<tr>
<td>The admin account has <code>AccountNotDelegated</code> True, or is in Protected Users</td>
<td>Its credentials can't be delegated, so remotely started moves fail even with correct delegation. Microsoft: "check the setting and group membership when you troubleshoot delegation issues"</td>
</tr>
<tr>
<td>0x8009030D "The credentials supplied to the package were not recognized"</td>
<td>Microsoft's text: "Make sure the operation is initiated on the source host of the migration, or the source host is configured to use Kerberos for the authentication of migration connections and constrained delegation is enabled for the host in active directory."</td>
</tr>
<tr>
<td><code>setspn -L</code> shows no Microsoft Virtual System Migration Service SPN</td>
<td>Microsoft's guide: "Ensure the required Service Principal Names (SPNs) are registered."</td>
</tr>
<tr>
<td>0x80090322 within one cluster or between standalone hosts</td>
<td>Kerberos got a ticket for a different machine than the one that answered: check name resolution and SPNs the way the <a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">Target Principal Name post</a> does (our reading)</td>
</tr>
</tbody></table>
<p>Two timing notes. Delegation changes take effect after "The changes replicate to the domain controllers that the servers running Hyper-V are logged into." and "The domain controller issues a new Kerberos ticket." Microsoft's 0x8009030D fix says to "Wait up to 15 minutes for Keberos tickets to time out" (sic) or purge the computer's tickets (Step 11). A fix that "didn't work" five minutes after the change may simply not have reached the host yet (our reading). Microsoft's blog also confirms that Hyper-V Manager and <code>Move-VM</code> fail the same way, so switching tools doesn't help.</p>
<hr />
<h2>Step 9: Does TCP 6600 connect from the source to the destination? (read-only)</h2>
<p>Microsoft's checks use TCP 6600 for the Hyper-V migration listener and 3343 for clustering. Microsoft's form of the check is <code>netstat -ano | findstr /I /C:"6600"</code>. The same reading in PowerShell, plus a test from the source:</p>
<pre><code class="language-powershell"># On the destination: is the Hyper-V Virtual Machine Management service running and listening
Get-Service -Name vmms | Format-Table Name, Status, StartType -AutoSize
Get-NetTCPConnection -LocalPort 6600 -State Listen -ErrorAction SilentlyContinue |
    Format-Table LocalAddress, LocalPort, State, OwningProcess -AutoSize

# Clustered nodes: the cluster service port
Get-NetTCPConnection -LocalPort 3343 -State Listen -ErrorAction SilentlyContinue |
    Format-Table LocalAddress, LocalPort, State -AutoSize

# From the source: does 6600 answer by name and on the migration network address
Test-NetConnection -ComputerName HV02 -Port 6600
Test-NetConnection -ComputerName 192.168.10.12 -Port 6600

# On both hosts: the Hyper-V firewall rules and their state (the wildcard is our form)
Get-NetFirewallRule -DisplayName '*Hyper-V*' |
    Format-Table DisplayName, Enabled, Direction, Action -AutoSize
</code></pre>
<p>Read the result:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>Nothing listening on 6600 on the destination</td>
<td>Migration isn't enabled there (Step 7), the service is stopped, or Group Policy disabled the listener</td>
</tr>
<tr>
<td>"The Virtual Machine Management Service disabled the listener for Virtual Machine migration connections."</td>
<td>Microsoft's fix starts with <code>gpupdate /force</code>; if that reports that "The processing of Group Policy failed", the local policy file is suspect: "The file must start with the PReg signature." The <a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying post</a> covers the rest of that path</td>
</tr>
<tr>
<td>Listening, but <code>Test-NetConnection</code> fails from the source</td>
<td>A firewall between the hosts or on the destination. 0x8007274C means the connection attempt timed out; Microsoft also lists a corrupt Registry.pol for it</td>
</tr>
<tr>
<td>0x8007274D "No connection could be made because the target machine actively refused it"</td>
<td>The source reached an address where nothing listens on 6600: often a different network than the one in the destination's <code>Get-VMMigrationNetwork</code> list (our reading)</td>
</tr>
<tr>
<td>0x80072746 "An existing connection was forcibly closed by the remote host."</td>
<td>The connection opened and then dropped: Microsoft points to the listening ports and the cluster network metrics</td>
</tr>
<tr>
<td>0x8007271D</td>
<td>Microsoft: "Check Firewall settings and Antivirus exclusion." Its Hyper-V antivirus guidance lists process exclusions for vmms.exe and vmwp.exe</td>
</tr>
<tr>
<td>Events 20406 and 280, "The client cannot connect to the destination specified in the request"</td>
<td>WinRM between the hosts. Microsoft: "Ensure hosts can resolve and reach each other via hostname/IP (test with ping)." and use <code>winrm quickconfig</code></td>
</tr>
</tbody></table>
<p><code>Test-NetConnection -Port</code> tests TCP only. The listener answering by name but not on the migration address (or the reverse) is the most useful split you can get here: it tells you whether the problem is the path or the host (our reading).</p>
<hr />
<h2>Step 10: Can the destination hold the VM? (read-only)</h2>
<p>The last group of 21502 messages is about the destination itself: memory, the storage path, cluster ownership, user rights, and leftover state.</p>
<pre><code class="language-powershell"># On the source: the VM's memory settings
Get-VMMemory -VMName APP01 | Format-List *

# On the destination: what's already running and how much memory it holds
Get-VM -ComputerName HV02 | Where-Object { $_.State -eq 'Running' } |
    Format-Table Name, State, MemoryAssigned -AutoSize

# Where the VM's disks live, and whether the destination path exists
Get-VMHardDiskDrive -VMName APP01 | Format-Table VMName, ControllerType, Path -AutoSize
Invoke-Command -ComputerName HV02 -ScriptBlock { Test-Path -Path 'D:\VMs' }

# Clustered: which nodes may own the VM and its configuration (Microsoft's form)
Get-ClusterResource -Name "Virtual Machine APP01" | Get-ClusterOwnerNode
Get-ClusterResource -Name "Virtual Machine Configuration APP01" | Get-ClusterOwnerNode
Get-ClusterNode | Format-Table Name, State -AutoSize
</code></pre>
<p>Read the result:</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
</tr>
</thead>
<tbody><tr>
<td>0x800705AA "Insufficient system resources exist to complete the requested service." or 0x8007000E "Ran out of memory"</td>
<td>Microsoft: "Reduce the memory assigned to the virtual machine or turn off some virtual machines on the destination node."</td>
</tr>
<tr>
<td>0x80071398, destination missing from <code>Get-ClusterOwnerNode</code></td>
<td>Microsoft: "The owner node must have all cluster nodes." Check both the Virtual Machine and the Virtual Machine Configuration resource</td>
</tr>
<tr>
<td>Event 20413, fails at 90% to 100%</td>
<td>Microsoft: the nodes have different system drive letters, and the destination can't register the configuration at its path</td>
</tr>
<tr>
<td>Folder errors on the destination, or the path test returns False</td>
<td>Microsoft's checklist: "Compare permissions on the folders containing the virtual machines files with a working host with the same operating system level." Storage moves also need cifs delegation (Step 8)</td>
</tr>
<tr>
<td>0x80070569 logon type not granted</td>
<td>Microsoft's checklist: under User Rights Assignment, <strong>Create symbolic links</strong> should list Administrators and NT VIRTUAL MACHINE\Virtual Machines, and <strong>Log on as a service</strong> should list NT SERVICE\ALL SERVICES and NT VIRTUAL MACHINE\Virtual Machines. A security baseline GPO that rewrites those lists breaks this</td>
</tr>
<tr>
<td>Shared VHDX attached</td>
<td>Microsoft: "Shared virtual disks can only be used in clustered VM roles and can't be migrated using standard methods."</td>
</tr>
<tr>
<td>vTPM or shielded VM, "The key protector for the virtual machine could not be unwrapped."</td>
<td>The destination doesn't have the key protector certificates; Microsoft's fix is to export them from the source and import them on the destination</td>
</tr>
<tr>
<td>Saved state that can't be read, a VM stuck in "Backing up", or a broken checkpoint chain</td>
<td>Fix the VM's state first; the <a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed post</a> covers checkpoints and AVHDX chains</td>
</tr>
<tr>
<td>0x800705B4 timeout after a node crashed</td>
<td>Microsoft's entry: symbolic links for the VM left on the destination; its workaround is quick migrations in both directions or a configuration version upgrade</td>
</tr>
</tbody></table>
<p><code>MemoryAssigned</code>, <code>Path</code>, and the other property lists above are our forms; Microsoft's own wording for the memory case is simply "Check the available memory on the destination host."</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>Tempting move</th>
<th>Why it's premature (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>Turn on processor compatibility for every VM</td>
<td>Hides CPU features from every workload, needs each VM shut down, and doesn't help with a switch, version, authentication, or connection failure. Microsoft recommends it "only during the migration process"</td>
</tr>
<tr>
<td>Switch every host to CredSSP</td>
<td>Trades a delegation problem for a sign-in-on-the-source problem, and fails on Windows Server 2025 sources with Credential Guard</td>
</tr>
<tr>
<td>Set <code>UseAnyNetworkForMigration</code> to $true</td>
<td>Sends unencrypted VM memory over networks you didn't pick, and doesn't fix a listener or delegation problem</td>
</tr>
<tr>
<td>Turn off the host firewall</td>
<td>Proves nothing about a listener that isn't there, and leaves the host open while you test</td>
</tr>
<tr>
<td>Upgrade the VM configuration version</td>
<td>Can't be undone, and blocks the VM from every older host</td>
</tr>
<tr>
<td>Rename the switch on the source</td>
<td>Breaks every other VM on that host that uses the old name; match the destination to the source or both to a standard</td>
</tr>
<tr>
<td>Unconstrained delegation for the hosts</td>
<td>Wider than Hyper-V needs, and Credential Guard blocks Kerberos unconstrained delegation</td>
</tr>
<tr>
<td>Restart VMMS or reboot the destination</td>
<td>Clears the evidence (the listener state, the events) without changing the setting that caused it</td>
</tr>
<tr>
<td>Quick migration "because live migration is broken"</td>
<td>Works for some causes because it saves and restores the VM, but hides which link was broken; the next live migration fails the same way</td>
</tr>
<tr>
<td>Evict and re-add the cluster node</td>
<td>Changes ownership, networks, and possibly the cluster log you needed, before anyone read the inner error</td>
</tr>
</tbody></table>
<hr />
<h2>Step 11: Change one link, then prove it</h2>
<p>Before any change, write down what you're about to change: the <code>Get-VMHost</code> migration settings, the switch list, the VM's processor and version settings, and the delegation lists from Steps 4-8. Then change only the link Steps 1-10 pointed to.</p>
<p><strong>Switches and media (Step 4).</strong> Create or rename the switch on the destination so it matches the name the VM uses, with the same type and uplink, following Microsoft's resolution: "If the virtual switch doesn't exist, create or rename it with the same name and settings such as in other hosts." In a cluster, refresh the VM configuration in Failover Cluster Manager afterward. For an ISO the destination can't open, eject it:</p>
<pre><code class="language-powershell"># On the source: remove the ISO from the VM's DVD drive (Microsoft's fix is to select None)
Get-VMDvdDrive -VMName APP01 | Set-VMDvdDrive -Path $null
</code></pre>
<p>For a clustered VM on an Internal or Private switch, Microsoft's workaround is to clear the adapter's <strong>Protected network</strong> option, which in PowerShell is <code>Set-VMNetworkAdapter -NotMonitoredInCluster $true</code> for that adapter. That stops the cluster from checking that adapter's connectivity, so use it only for adapters that are meant to be host-local (our reading).</p>
<p><strong>Processor (Step 5).</strong> Shut the VM down, turn on compatibility for that VM only, start it, and move it:</p>
<pre><code class="language-powershell"># Microsoft's forms; the VM must be off
Stop-VM -Name APP01
Set-VMProcessor -VMName APP01 -CompatibilityForMigrationEnabled $true
# Windows Server 2025 cluster, configuration version 10.0 or later: dynamic mode instead
# Set-VMProcessor -VMName APP01 -CompatibilityForMigrationEnabled $true -CompatibilityForMigrationMode CommonClusterFeatureSet
Start-VM -Name APP01
</code></pre>
<p>After the hardware refresh is done, Microsoft recommends turning it back off so the VM can use the full processor; that's another short shutdown. If BIOS settings or <code>Get-SpeculationControlSettings</code> results differ between hosts, fix the host that's out of line instead of masking it on every VM. Across microcode levels, move old to new.</p>
<p><strong>Configuration version (Step 6).</strong> Don't downgrade; you can't. Keep the VM on hosts that support its version, or, once every host in its path runs the newer Windows Server and the cluster functional level is raised, upgrade it with Microsoft's <code>Update-VMVersion &lt;vmname&gt;</code> (VM shut down, one-way).</p>
<p><strong>Settings and networks (Step 7).</strong> Enable migration on the host that's missing it, make the authentication protocol the same on both hosts, and add the shared migration network to the destination. Microsoft's PowerShell form:</p>
<pre><code class="language-powershell"># On each host that needs it (Microsoft's three-cmdlet example)
Enable-VMMigration
Set-VMMigrationNetwork 192.168.10.1
Set-VMHost -VirtualMachineMigrationAuthenticationType Kerberos
</code></pre>
<p>Raise the simultaneous migration limit only if the host and the network can take it; Microsoft's note on that setting is "Consider host performance when changing this number." In a cluster, change the live migration network order in Failover Cluster Manager (<strong>Networks</strong> &gt; <strong>Live Migration Settings</strong>) or the <code>MigrationExcludeNetworks</code> parameter, not each host. For a cross-cluster move that picked a private network, Microsoft's fix is "Change the Live Migration Network order for cross-cluster and change it back after the migration."</p>
<p><strong>Authentication (Step 8).</strong> Prefer Kerberos with constrained delegation. With a Domain Admins account, on the source host's computer object in Active Directory Users and Computers: <strong>Delegation</strong> tab &gt; <strong>Trust this computer for delegation to the specified services only</strong> &gt; <strong>Use any authentication protocol</strong> &gt; <strong>Add</strong> the destination host, and select <strong>cifs</strong> and <strong>Microsoft Virtual System Migration Service</strong>. Then repeat on the destination for the source. Set both hosts to Kerberos (Step 7 example). Then let the change take effect, either by waiting or, on each host, with Microsoft's ticket purge for the computer account's logon session:</p>
<pre><code class="language-console">REM Purge the host's own (SYSTEM, 0x3e7) Kerberos tickets so it requests new ones with the delegation change
klist purge -li 0x3e7
</code></pre>
<p>Microsoft's <code>klist</code> reference warns: "Purging tickets destroys all tickets that you have cached, so use this attribute with caution." On Windows Server 2025, don't disable Credential Guard to bring CredSSP back; Microsoft's resolution is that "Kerberos Constrained Delegation and Resource-Based Kerberos Constrained Delegation are recommended" instead. If the admin account is in Protected Users or marked sensitive, start the move while signed in on the source host, or use an account meant for that, rather than removing the protection (our reading). If the Microsoft Virtual System Migration Service SPN is missing, Microsoft's guide registers it with <code>setspn -s Microsoft Virtual System Migration Service/&lt;FQDN&gt; &lt;ComputerName&gt;</code>.</p>
<p><strong>Connection (Step 9).</strong> If the listener was disabled by Group Policy, run <code>gpupdate /force</code> on that host and read the result; if Registry.pol is corrupt, Microsoft's fix is to restore it from a backup or copy the GroupPolicy folder from a working server. Open TCP 6600 between the hosts on the migration network (and 3343 between cluster nodes) in whatever firewall sits between them. Add Microsoft's Hyper-V process exclusions to third-party antivirus.</p>
<p><strong>Destination capacity (Step 10).</strong> Free memory on the destination or pick another one; add the missing node to the possible owners of both cluster resources; give the destination the same system drive letter as the other nodes; restore the default user rights for the NT VIRTUAL MACHINE\Virtual Machines group in the GPO that removed them; fix the folder permissions on the destination path to match a working host.</p>
<p><strong>Then prove it.</strong> Run <code>Compare-VM -Name APP01 -DestinationHost HV02</code> again and confirm the <code>Incompatibilities</code> list is empty, then move the VM the way the user did (same tool, same host it was started from):</p>
<pre><code class="language-powershell"># Standalone hosts (Microsoft's Move-VM form; add -IncludeStorage for a storage move)
Move-VM -Name APP01 -DestinationHost HV02

# Clustered VM (Microsoft's form)
Move-ClusterVirtualMachineRole -Name "APP01" -Node HV02 -MigrationType Live
</code></pre>
<p>Pass criteria (our reading): the move completes, <code>Get-VM -ComputerName HV02</code> shows the VM Running there, no new 21502 or worker errors on either host for that time, the VM's network works on the destination (no Event 21024), and the move back to the source works too. Then retry the other VMs from the ticket.</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Switch name missing on the destination</td>
<td>Create or rename it to the standard name, same type and uplink</td>
<td>Renaming the source switch and breaking its other VMs</td>
</tr>
<tr>
<td>Processor features differ, same vendor</td>
<td>Compatibility on for the VMs that must move live, then off again</td>
<td>Compatibility on for every VM permanently</td>
</tr>
<tr>
<td>Intel to AMD (or back)</td>
<td>A planned shutdown and cold move</td>
<td>Hoping a setting makes it live-migratable</td>
</tr>
<tr>
<td>Configuration version newer than the destination</td>
<td>Keep the VM on newer hosts until the old ones are retired</td>
<td>Upgrading versions mid-migration, then needing to go back</td>
</tr>
<tr>
<td>CredSSP failing from a remote console</td>
<td>Kerberos with constrained delegation, both directions</td>
<td>CredSSP everywhere with "sign in first" as the process</td>
</tr>
<tr>
<td>Windows Server 2025 source, CredSSP</td>
<td>Kerberos constrained or resource-based constrained delegation</td>
<td>Turning off Credential Guard</td>
</tr>
<tr>
<td>No shared migration network</td>
<td>Add a dedicated, isolated migration network to both hosts</td>
<td><code>UseAnyNetworkForMigration</code> set to $true</td>
</tr>
<tr>
<td>Nothing listening on 6600</td>
<td>Re-enable migration, fix the GPO, open the port between hosts</td>
<td>Turning off the host firewall</td>
</tr>
<tr>
<td>Destination out of memory</td>
<td>Move other VMs off it or choose another host</td>
<td>Shrinking the VM's memory without asking its owner</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What is Event ID 21502 in Hyper-V?</strong>
It's the summary event Hyper-V logs when a live migration fails (most often seen on clustered hosts), and the real reason is in its message text. Microsoft lists about twenty different causes under that one ID, from "a virtual switch used by the VM doesn't exist on the destination node" to processor features, Kerberos errors, memory, and ISO files. Read the full message on both hosts and run <code>Compare-VM</code> (Steps 2 and 3).</p>
<p><strong>Why does Hyper-V live migration fail with a processor compatibility error?</strong>
The destination can't offer every CPU feature the running VM saw when it started. Turn on processor compatibility for that VM (it has to be off to change the setting), make the hosts' BIOS settings and speculation control results match, and move from older to newer microcode. It won't help between Intel and AMD hosts (Step 5).</p>
<p><strong>Should I use CredSSP or Kerberos for live migration?</strong>
Kerberos with constrained delegation, set on both hosts for each other with cifs and Microsoft Virtual System Migration Service. CredSSP only works when you're signed in on the source host, and Microsoft says "you can't use CredSSP-based live migration with Hyper-V after upgrading to Windows Server 2025" with Credential Guard on (Step 8).</p>
<p><strong>Why does live migration fail with 0x8009030E "No credentials are available in the security package"?</strong>
Either CredSSP is in use and the move was started from somewhere other than the source host, or Kerberos is in use and delegation is missing, one-way, set to "Kerberos only", pointed at the wrong service, or blocked because the admin account can't be delegated (Protected Users or sensitive). Read <code>msDS-AllowedToDelegateTo</code> for both hosts (Step 8).</p>
<p><strong>Why does live migration work one way but not the other?</strong>
Usually the VM's configuration version was upgraded on the newer host, or the newer host has CPU features or microcode the older host lacks. Compare <code>Get-VM</code> versions with <code>Get-VMHostSupportedVersion</code> on the older host and check processor compatibility (Steps 5 and 6). With CredSSP, it can also be because you're signed in on one host but not the other.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-2mak">The Target Principal Name Is Incorrect</a> (when the migration fails with 0x80090322 and the Kerberos name is the problem)</li>
<li><a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed</a> (when a broken checkpoint, saved state, or AVHDX chain blocks the move)</li>
<li><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> (when the migration listener is disabled because policy processing failed)</li>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (the same name, port, and firewall reading for RPC between servers)</li>
</ul>
<h2>Free checklist</h2>
<p>Turning on processor compatibility, CredSSP, and any-network migration on every host before anyone read the inner error is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=hyper-v-live-migration-failed">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=hyper-v-live-migration-failed</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=hyper-v-live-migration-failed">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=hyper-v-live-migration-failed</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-10 favor read-only checks: <code>Get-WinEvent</code>, <code>Get-ClusterLog</code>, <code>Compare-VM</code>, <code>Get-VMNetworkAdapter</code>, <code>Get-VMSwitch</code>, <code>Get-VMDvdDrive</code>, <code>Get-VMProcessor</code>, <code>Get-VMSnapshot</code>, <code>Get-VM</code>, <code>Get-VMHostSupportedVersion</code>, <code>Get-VMIntegrationService</code>, <code>Get-VMHost</code>, <code>Get-VMMigrationNetwork</code>, <code>Get-ClusterNetwork</code>, <code>Get-Cluster</code>, <code>Get-ClusterParameter</code>, <code>Get-ADComputer</code>, <code>Get-ADUser</code>, <code>Get-ADGroupMember</code>, <code>setspn -L</code>, <code>Get-Service</code>, <code>Get-NetTCPConnection</code>, <code>Test-NetConnection</code>, <code>Get-NetFirewallRule</code>, <code>Get-VMMemory</code>, <code>Get-VMHardDiskDrive</code>, <code>Test-Path</code>, <code>Get-ClusterResource</code>, <code>Get-ClusterOwnerNode</code>, and <code>Get-ClusterNode</code>. Writing the cluster log creates files on every node. Changing switches, DVD media, processor compatibility, configuration versions, migration settings and networks, cluster network order, AD delegation, SPNs, Kerberos tickets, Group Policy, firewall rules, antivirus exclusions, user rights, cluster owners, or VM memory, and stopping, starting, or moving VMs, are deliberate changes. Change one link at a time, prove it with <code>Compare-VM</code> and a move in both directions, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[RD Gateway Not Working: A Read-First Certificate, Port, CAP/RAP, and NPS Triage]]></title><description><![CDATA[RD Gateway not working? Read the public name and port 443, the certificate it serves, CAP and RAP events, and NPS MFA timeouts before you change anything.]]></description><link>https://adminpackstudio.hashnode.dev/rd-gateway-not-working-a-read-first-certificate-port-cap-rap-and-nps-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/rd-gateway-not-working-a-read-first-certificate-port-cap-rap-and-nps-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[networking]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 06:42:40 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since the certificate renewal on Friday, nobody outside the office can connect through the gateway. Most users get 'the Remote Desktop Gateway server is temporarily unavailable', two get a certificate warning first, and from inside the office everything works. The MFA prompt on the phone also stopped showing up for one team. A forum post says to reinstall the RD Gateway role, turn off the Windows Firewall on the gateway, change the CAP and RAP to allow everyone to everything, and uninstall the NPS extension. Can we do that?"</p>
<p>Not yet. <strong>"RD Gateway not working" is almost never the role itself. A connection through Remote Desktop Gateway is a chain of separate checks, and each one fails with a different symptom: the public name resolves to the gateway and TCP 443 reaches it, the certificate it serves matches that name and is trusted and current on the client, the TSGateway service is listening, the user passes a connection authorization policy (CAP), the target passes a resource authorization policy (RAP), NPS and the MFA extension answer inside the RADIUS timeout, and the gateway can reach the target on 3389.</strong> Microsoft states the authorization part plainly: "For users to be granted access to network resources through a Remote Desktop Gateway, they must meet the conditions specified in one RD Connection Authorization Policy (RD CAP) and one RD Resource Authorization Policy (RD RAP)." Reinstalling the role throws away the CAPs, RAPs, and certificate binding you need to read, turning off the firewall proves nothing about the edge firewall in front of it, an allow-everything CAP and RAP opens the gateway to the internet, and removing the NPS extension removes MFA instead of fixing it. Work through it as a chain: <strong>which user, which client, which name, which error</strong> → <strong>does the public name reach the gateway on 443?</strong> → <strong>is the certificate it serves the right one, trusted, current, and checkable?</strong> → <strong>is the gateway service healthy and listening?</strong> → <strong>does the user pass a CAP?</strong> → <strong>does the target pass a RAP?</strong> → <strong>do NPS and the MFA extension answer in time?</strong> → <strong>can the gateway reach the target?</strong> → <strong>is UDP 3391 working, and why is Gateway Manager empty?</strong> <code>Resolve-DnsName</code>, <code>Test-NetConnection</code>, <code>Test-Certificate</code>, <code>Get-RDCertificate</code>, <code>netsh http show sslcert</code>, <code>Get-CimInstance</code> against the RD Gateway WMI classes, and <code>Get-WinEvent</code> on the TerminalServices-Gateway log are the sources for that chain. Change the certificate, the CAP, and the firewall at once and you'll never know which one was broken.</p>
<p>If the session host itself refuses users after the gateway lets them through with a licensing message, <a href="https://dev.to/adminpackstudio/remote-desktop-licensing-mode-is-not-configured-a-read-first-license-server-cal-and-grace-period-2j3l">Remote Desktop Licensing Mode Is Not Configured</a> covers that side. If NPS logs Event 6273 for the gateway's RADIUS requests, <a href="https://dev.to/adminpackstudio/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage-26i6">NPS Event 6273 Reason Code 16 or 22</a> covers reason codes. This post covers <strong>"users can't connect through a Windows Server RD Gateway (usually from outside the network): 'temporarily unavailable', a certificate expired or untrusted warning, an authorization failure, no MFA prompt, a session that fails after authorization, or an empty RD Gateway Manager, and you need a read-first path before you reinstall the role, replace the certificate, open the policies, or change NPS."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Inside working proves little.</strong> Internal clients may skip the gateway entirely: <code>BypassLocal</code> "Indicates whether authorized remote users bypass the RD Gateway server for local connections." Test from outside (Step 2).</li>
<li><strong>The certificate has to match the name clients type.</strong> Microsoft: "The name of the certificate must match the FQDN used to access RD Gateway" (Step 3). A renewed certificate that isn't bound to 443 is still the old one.</li>
<li><strong>Two policies, two events.</strong> A CAP failure is event 201, a RAP failure is event 301; read the TerminalServices-Gateway log before you touch either (Steps 5 and 6).</li>
<li><strong>MFA needs time and the right methods.</strong> Microsoft's RD Gateway guide says to change the RADIUS timeout "from 3 to a value between 30 and 60 seconds", and SMS codes don't work through the gateway (Step 7).</li>
<li><strong>Authorized but not connected is the target, not the gateway.</strong> Event 304 means the user passed both policies and the gateway couldn't reach the computer (Step 8).</li>
<li><strong>UDP 3391 is a second transport, not the only one.</strong> Read which transport live connections use before you blame UDP (Step 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which user, which client and network, which gateway name, which exact message, since when</td>
<td>"The gateway is down" with no user, name, or time written down</td>
</tr>
<tr>
<td>2. Name and TCP 443</td>
<td>The public name resolves to the gateway's public address and 443 reaches it from outside</td>
<td>Works inside, fails outside; "temporarily unavailable" for everyone outside</td>
</tr>
<tr>
<td>3. Certificate</td>
<td>The certificate served on 443 matches the name, is trusted by the client, is current, and its revocation can be checked</td>
<td>Certificate expired, untrusted, or name mismatch warnings; some clients fail, others don't</td>
</tr>
<tr>
<td>4. Gateway service</td>
<td>TSGateway running, listening on 443 (and 3391), configured, under its connection limit</td>
<td>Every connection fails at once, including from inside through the gateway</td>
</tr>
<tr>
<td>5. CAP</td>
<td>The user (and, if set, the client computer) is in a group of an enabled CAP</td>
<td>Event 201; "not authorized" before any session starts</td>
</tr>
<tr>
<td>6. RAP</td>
<td>The target name, as typed, is in a RAP's resource list for that user, on that port</td>
<td>Event 301; works by IP or FQDN, fails by short name</td>
</tr>
<tr>
<td>7. NPS and MFA</td>
<td>The central NPS answers the gateway inside the timeout, and the extension can reach Microsoft Entra ID</td>
<td>No phone prompt, prompt arrives late, or "temporarily unavailable" after the password</td>
</tr>
<tr>
<td>8. Gateway to target</td>
<td>The gateway reaches the target on 3389, and the target accepts the user</td>
<td>Event 304; or the session host's own error after authorization</td>
</tr>
<tr>
<td>9. UDP and monitoring</td>
<td>UDP 3391 passes both ways, and you're looking at the right gateway</td>
<td>Slow or HTTP-only sessions; RD Gateway Manager shows no connections</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one link, one change</td>
<td>Role reinstalled, same failure, and the policies are gone</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which user and which client</strong></td>
<td>One user, one group, everyone; a managed laptop, a home PC, a Mac, a web (RD Web) launch. Home PCs don't trust an internal CA (Step 3) and aren't in AD groups a CAP may require (Step 5)</td>
</tr>
<tr>
<td><strong>Which network</strong></td>
<td>Outside, inside, on VPN. Inside may bypass the gateway (Step 2)</td>
</tr>
<tr>
<td><strong>Which gateway name</strong></td>
<td>The exact FQDN in the client or the .rdp file, and the target name typed (short name, FQDN, or IP; Step 6)</td>
</tr>
<tr>
<td><strong>Exact message and when it appears</strong></td>
<td>Before the password prompt (name, 443, certificate), after the password (CAP, NPS, MFA), or after a pause with the session starting (RAP, target)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>A certificate renewal, a firewall or ISP change, a new public IP, an MFA rollout, a new NPS server, a Windows update</td>
</tr>
<tr>
<td><strong>Time of one failing attempt</strong></td>
<td>So you can match it to events 200-304 on the gateway and 6272/6273 on NPS</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Everyone outside fails, everyone inside works</td>
<td>The public path (DNS, NAT, edge firewall) or internal clients bypassing the gateway</td>
<td>Step 2</td>
</tr>
<tr>
<td>A certificate expired, untrusted, or name mismatch warning</td>
<td>The certificate served on 443, not necessarily the one you imported</td>
<td>Step 3</td>
</tr>
<tr>
<td>Only personal or home devices fail</td>
<td>The CA isn't trusted there, revocation can't be checked from the internet, or a CAP requires a domain computer group</td>
<td>Steps 3 and 5</td>
</tr>
<tr>
<td>Everyone fails, inside through the gateway too</td>
<td>The gateway service, its listener, or its certificate binding</td>
<td>Steps 3 and 4</td>
</tr>
<tr>
<td>Fails right after the password, event 201 on the gateway</td>
<td>CAP (or the central NPS policy when CAPs are central)</td>
<td>Steps 5 and 7</td>
</tr>
<tr>
<td>Fails by short name, works by FQDN, event 301</td>
<td>RAP resource list</td>
<td>Step 6</td>
</tr>
<tr>
<td>No MFA prompt, or "temporarily unavailable" after the password while MFA is on</td>
<td>RADIUS timeout, the user's MFA method, or the NPS extension</td>
<td>Step 7</td>
</tr>
<tr>
<td>Event 300 then 304, or the session host shows its own error</td>
<td>The target or the path from the gateway to it</td>
<td>Step 8</td>
</tr>
<tr>
<td>Sessions work but feel slow, or RD Gateway Manager shows nothing</td>
<td>UDP 3391, or the wrong server or no gateway use at all</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Does the public name reach the gateway on TCP 443? (read-only)</h2>
<p>Microsoft's role page sets the public requirement: "The RD Gateway virtual machine must be accessible through a public IP address that allows inbound TCP connections to port 443 and inbound UDP connections to port 3391." Test it from where the users are, not from your desk. On an outside PC (a home connection or a phone hotspot):</p>
<pre><code class="language-powershell"># Read-only. From an OUTSIDE client: where does the public name point, and does TCP 443 answer? (name is an example)
Resolve-DnsName -Name rdg.contoso.com -Type A
Test-NetConnection -ComputerName rdg.contoso.com -Port 443
</code></pre>
<p>Then read what the deployment tells clients, on the RD Connection Broker (deployments managed from Server Manager):</p>
<pre><code class="language-powershell"># Read-only. On the RD Connection Broker: gateway mode, logon method, and the external FQDN handed to clients
Get-RDDeploymentGatewayConfiguration
</code></pre>
<p>Its documented output is <code>Gatewaymode</code> (Automatic, DoNotUse, or Custom), <code>LogonMethod</code>, and <code>GatewayExternalFQDN</code>. The matching <code>Set-</code> cmdlet also has <code>-BypassLocal</code>, which "Indicates whether authorized remote users bypass the RD Gateway server for local connections." With that on, an inside test can succeed without ever touching the gateway (our reading), so "it works from the office" is not a gateway test.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The public name resolves to an old address, or not at all from outside</td>
<td>The public DNS record wasn't updated after an ISP or firewall change</td>
<td>Fix the public record (Step 10)</td>
</tr>
<tr>
<td>The name resolves correctly, TCP 443 fails from outside, works inside to the gateway's internal address</td>
<td>The edge firewall, NAT rule, or port forward</td>
<td>Fix the edge rule (Step 10)</td>
</tr>
<tr>
<td>TCP 443 answers, but Step 3 shows a certificate that isn't the gateway's</td>
<td>The forward lands on another server or a reverse proxy</td>
<td>Step 3, then the edge rule</td>
</tr>
<tr>
<td><code>GatewayExternalFQDN</code> differs from the public DNS name or the certificate name</td>
<td>RD Web launches hand clients a name that won't match</td>
<td>Step 3; fix with <code>Set-RDDeploymentGatewayConfiguration</code> (Step 10)</td>
</tr>
<tr>
<td><code>Gatewaymode</code> is DoNotUse</td>
<td>RD Web launches don't use the gateway at all; outside clients try 3389 directly</td>
<td>Step 10</td>
</tr>
<tr>
<td>Everything here passes</td>
<td>The path is fine</td>
<td>Step 3</td>
</tr>
</tbody></table>
<p><code>Test-NetConnection -Port</code> only proves TCP: its <code>-Port</code> parameter "Specifies the TCP port number on the remote computer." UDP 3391 needs a different test (Step 9).</p>
<hr />
<h2>Step 3: Is the certificate it serves the right one, trusted, current, and checkable? (read-only)</h2>
<p>This is where most "it broke after the renewal" tickets end. The client checks the certificate the gateway serves on 443, not the one you imported last week. Microsoft's requirements for the gateway certificate (from the original TS Gateway guidance, still the clearest list): "The name in the Subject line of the server certificate (certificate name, or CN) must match the DNS name that the client uses to connect to the TS Gateway server, unless you are using wildcard certificates or the SAN attributes of certificates.", "The certificate has not expired.", and "The certificate must be trusted on clients." The current RDS certificates page adds the usage: "Issued for Server Authentication (EKU 1.3.6.1.5.5.7.3.1)."</p>
<p>On the RD Gateway, compare the binding, the gateway's own setting, and the store:</p>
<pre><code class="language-powershell"># Read-only. On the RD Gateway: every HTTP.sys SSL binding (look at the :443 entries and their certificate hash)
netsh http show sslcert

# The certificate hash the RD Gateway service uses (CertHash), as a thumbprint string (our form)
$gw = Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayServerSettings
$thumb = ($gw.CertHash | ForEach-Object { $_.ToString('X2') }) -join ''
$thumb

# Server authentication certificates in the computer store: names, expiry, thumbprints
Get-ChildItem -Path Cert:\LocalMachine\My -SSLServerAuthentication |
  Select-Object Subject, DnsNameList, NotAfter, Thumbprint, Issuer | Format-List

# Does the certificate the gateway uses pass SSL policy for the public name? (name is an example)
Test-Certificate -Cert "Cert:\LocalMachine\My\$thumb" -Policy SSL -DNSName rdg.contoso.com
</code></pre>
<p><code>netsh http show sslcert</code> "Lists SSL server certificate bindings along with their associated client certificate policies." <code>CertHash</code> "Specifies the certificate hash for HTTPS binding on port 443 in IIS." <code>Test-Certificate -DNSName</code> "Specifies the DNS name to verify as valid for the certificate." In a deployment managed from Server Manager, read what the deployment thinks too:</p>
<pre><code class="language-powershell"># Read-only. On the RD Connection Broker: the certificate assigned to the RD Gateway role
Get-RDCertificate -Role RDGateway | Format-List Subject, SubjectAlternateName, IssuedBy, ExpiresOn, Thumbprint, Level
</code></pre>
<p><code>Test-Certificate</code> runs on the gateway, which trusts your internal CA and can reach your internal CRLs. A home PC can't. So check one failing outside client as well: export the gateway certificate's public part (.cer, no private key) and run Microsoft's chain check there:</p>
<pre><code class="language-console">:: Read-only. On a failing OUTSIDE client, with the exported gateway certificate (file name is an example)
certutil -urlfetch -verify rdg.cer
</code></pre>
<p><code>-urlfetch</code> is documented as "Retrieve and verify AIA Certs and CDP CRLs." So it shows whether that client can build the chain and download the CRL from where it sits.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The <code>netsh</code> hash for :443 or <code>$thumb</code> isn't the new certificate's thumbprint</td>
<td>The renewal imported a certificate, but the gateway still binds and serves the old one (our reading)</td>
<td>Bind the new one (Step 10)</td>
</tr>
<tr>
<td><code>NotAfter</code> or <code>ExpiresOn</code> is in the past</td>
<td>Expired; clients refuse it</td>
<td>Replace it (Step 10)</td>
</tr>
<tr>
<td><code>Test-Certificate -DNSName</code> returns False</td>
<td>The name clients use isn't in the subject or SAN. "The name of the certificate must match the FQDN used to access RD Gateway, whether the FQDN is the public IP address' externally facing DNS name or the CNAME DNS record pointing to the public IP address."</td>
<td>A certificate with the right name, or the right name in DNS and the client</td>
</tr>
<tr>
<td><code>Level</code> is Untrusted</td>
<td>"The role service is configured with a self-signed certificate." Outside clients won't trust it</td>
<td>A publicly trusted certificate (Step 10)</td>
</tr>
<tr>
<td><code>certutil</code> chain fails on a home PC, passes on a domain PC</td>
<td>A private CA. Microsoft: "If you use a privately issued certificate, you'll need to make sure to configure all clients with the certificate's trust chain beforehand."</td>
<td>A public certificate for the gateway, or deploy the root to those clients</td>
</tr>
<tr>
<td><code>certutil</code> builds the chain but can't fetch the CRL from outside</td>
<td>The CDP is only reachable inside, so outside clients can't check revocation (our reading); <a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline 0x80092013</a> covers CDP reachability</td>
<td>Publish the CRL where outside clients can reach it</td>
</tr>
<tr>
<td><code>SslBridging</code> (Step 4) is 1 or 2</td>
<td>A device in front terminates TLS (values: "HTTPS to HTTP bridging." and "HTTPS to HTTPS bridging."), so clients see the proxy's certificate (our reading)</td>
<td>Check the certificate on the proxy</td>
</tr>
<tr>
<td>All pass on the gateway and on an outside client</td>
<td>The certificate is fine</td>
<td>Step 4</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Is the gateway service healthy and listening? (read-only)</h2>
<pre><code class="language-powershell"># Read-only. On the RD Gateway: the gateway service and the local NPS service it uses
Get-Service -Name TSGateway, IAS

# Listeners: TCP 443 (HTTPS) and UDP 3391 (RDP over UDP)
Get-NetTCPConnection -LocalPort 443 -State Listen
Get-NetUDPEndpoint -LocalPort 3391 -ErrorAction SilentlyContinue

# Gateway settings from its WMI provider (namespace Root\CIMv2\TerminalServices)
$gw = Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayServerSettings
$gw | Select-Object IsConfigured, CentralCAPEnabled, MaxConnections, UnlimitedConnections, SslBridging, EnforceChannelBinding
</code></pre>
<pre><code class="language-powershell"># Read-only. Which transports are enabled and where they listen: 0 = RPC over HTTP, 1 = HTTP, 2 = UDP (our form)
foreach ($t in 0, 1, 2) {
  $on = Invoke-CimMethod -InputObject $gw -MethodName IsTransportEnabled -Arguments @{ TransportType = [uint16]$t }
  $ep = Invoke-CimMethod -InputObject $gw -MethodName GetIPAndPort -Arguments @{ TransportType = [uint16]$t }
  [pscustomobject]@{ Transport = $t; Enabled = $on.Enabled; IPAddress = $ep.IPAddress; Port = $ep.Port }
}

# Errors and warnings in the gateway log, last two days
Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TerminalServices-Gateway/Operational'; Level = 2, 3; StartTime = (Get-Date).AddDays(-2) } -MaxEvents 30 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>The RD Gateway WMI classes need an elevated session: "You must be a member of the Administrators group to use this class." The NPS service shows up as IAS (Microsoft's NPS extension guide says to "Restart the Network Policy Server (IAS) service."), and it's on the gateway by design: "By default, the role is already present on the computer configured as the Remote Desktop Gateway."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>TSGateway is stopped</td>
<td>Every connection fails at once</td>
<td>Start it, then read the gateway log for why it stopped</td>
</tr>
<tr>
<td>Nothing listening on 443, or <code>IsConfigured</code> False</td>
<td><code>IsConfigured</code> "Specifies if IIS and RPC settings required by the RD Gateway service are configured."</td>
<td>The IIS site or binding (Step 3 for the certificate), then Step 10</td>
</tr>
<tr>
<td>The HTTP transport port isn't 443</td>
<td>The port was changed: per Microsoft's ports page, TCP 443 "can be configured using the RD Gateway Management console." Clients and the edge rule must use the same port (our reading)</td>
<td>Match the client, the edge rule, and the gateway</td>
</tr>
<tr>
<td><code>UnlimitedConnections</code> False and <code>MaxConnections</code> low</td>
<td>New connections are refused at the limit. <code>MaxConnections</code> "Returns the maximum number of connections that are allowed through RD Gateway."</td>
<td>Raise it on purpose (Step 10)</td>
</tr>
<tr>
<td><code>CentralCAPEnabled</code> True</td>
<td>"Specifies whether central RD CAP servers are used for controlling this server." The CAP decision is made by the central NPS, so editing local CAPs changes nothing (our reading)</td>
<td>Steps 5 and 7</td>
</tr>
<tr>
<td>Gateway log errors at the time of the failures</td>
<td>The message names the failing part (certificate, service, authorization)</td>
<td>Map it back to Steps 3-8</td>
</tr>
<tr>
<td>All healthy</td>
<td>The gateway is up</td>
<td>Step 5</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Does the user pass a connection authorization policy (CAP)? (read-only)</h2>
<p>The gateway logs every authorization decision. Microsoft's RD Gateway guide reads the success events with <code>Get-WinEvent</code> on <code>Microsoft-Windows-TerminalServices-Gateway/Operational</code>; read the failures the same way, for one user, around the failure time:</p>
<pre><code class="language-powershell"># Read-only. On the RD Gateway: CAP, RAP, and connection events for one user, last 4 hours (user name is an example)
Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-TerminalServices-Gateway/Operational'; Id = 200, 201, 300, 301, 302, 303, 304; StartTime = (Get-Date).AddHours(-4) } -ErrorAction SilentlyContinue |
  Where-Object { $_.Message -like '*jsmith*' } |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>Microsoft's event table for the gateway (written for TS Gateway, same IDs): 200 "Successful Connection Authorization", 201 "Failed Connection Authorization", 300 "Successful Resource Authorization", 301 "Failed Resource Authorization", 302 "Successful User Connection to the Resource", 303 when the client disconnects, 202 when an administrator disconnects it, and 304 "Failed User Connection to the Resource". A good attempt reads 200, 300, 302 in that order.</p>
<p>Then read the local CAPs in the order the gateway evaluates them:</p>
<pre><code class="language-powershell"># Read-only. On the RD Gateway: local CAPs, in evaluation order
Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayConnectionAuthorizationPolicy |
  Sort-Object Order |
  Select-Object Order, Name, Enabled, UserGroupNames, ComputerGroupNames, PasswordAllowed, SmartcardAllowed, DeviceRedirectionType, AllowOnlySDRServers | Format-List
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Event 201 for the user</td>
<td>"The remote client could not connect to a TS Gateway server because the client did not meet the conditions specified in the TS CAPs." Read the message for the reason, then compare the user's groups with <code>UserGroupNames</code></td>
<td>Step 10 (group membership, not a wider CAP)</td>
</tr>
<tr>
<td>No 200 or 201 at all for the attempt</td>
<td>The request never reached authorization (our reading)</td>
<td>Steps 2-4, or the RADIUS timeout in Step 7</td>
</tr>
<tr>
<td><code>ComputerGroupNames</code> is set on the only matching CAP</td>
<td>"If a value is specified, then the client computer must belong to one of these computer groups for the user to access the RD Gateway server." Home and personal PCs fail by design</td>
<td>Decide whether those devices should connect at all</td>
</tr>
<tr>
<td><code>PasswordAllowed</code> False, users sign in with a password</td>
<td><code>PasswordAllowed</code> "Indicates if a password can be used to connect to the RD Gateway server."</td>
<td>Match the CAP to how users sign in</td>
</tr>
<tr>
<td>The CAP you expect has <code>Enabled</code> False</td>
<td><code>Enabled</code> "Indicates whether this RD CAP will be used to evaluate a user for authorization."</td>
<td>Find out who disabled it before you enable it</td>
</tr>
<tr>
<td><code>AllowOnlySDRServers</code> True and only some targets fail later</td>
<td><code>AllowOnlySDRServers</code> "Indicates whether connections allowed only to secure device redirection (SDR) RDS servers." Targets that don't enforce it are refused (our reading)</td>
<td>Step 8 for those targets</td>
</tr>
<tr>
<td><code>CentralCAPEnabled</code> True (Step 4)</td>
<td>This list isn't what decides; the central NPS network policy is</td>
<td>Step 7</td>
</tr>
<tr>
<td>Event 200 for the user</td>
<td>The CAP passed</td>
<td>Step 6</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Does the target pass a resource authorization policy (RAP)? (read-only)</h2>
<p>RAPs always live on the gateway itself: "RD RAPs can't use a central policy, as they're processed on the RD Gateway." Read them, and the resource groups they point at:</p>
<pre><code class="language-powershell"># Read-only. On EACH RD Gateway: RAPs and the resource groups behind them
Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayResourceAuthorizationPolicy |
  Select-Object Name, Enabled, UserGroupNames, ResourceGroupType, ResourceGroupName, PortNumbers | Format-List
Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayResourceGroup |
  Select-Object Name, AssociatedRAPs, Resources | Format-List
</code></pre>
<p><code>ResourceGroupType</code> is <code>RG</code> (a gateway-managed group, listed in <code>Win32_TSGatewayResourceGroup</code>), <code>CG</code> (an Active Directory computer group), or <code>ALL</code>. <code>Resources</code> is a "Semicolon-separated list of resources in this resource group."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Event 301, the user typed a short name, the resource group lists FQDNs</td>
<td>Microsoft's own example: "this issue might occur if the user attempts to connect to the computer by using its NetBIOS name when the TS RAP configured on the TS Gateway server uses an FQDN name for the computer."</td>
<td>Add the name users type, or have them use the FQDN (Step 10)</td>
</tr>
<tr>
<td><code>ResourceGroupType</code> CG, the target isn't in that AD computer group</td>
<td>The RAP doesn't cover it</td>
<td>Add the computer to the group (Step 10)</td>
</tr>
<tr>
<td>The target listens on a non-default port that isn't in <code>PortNumbers</code></td>
<td><code>PortNumbers</code> is a "List of semicolon-separated port numbers that are allowed for this policy."</td>
<td>Add the port to that RAP (Step 10)</td>
</tr>
<tr>
<td>The user passes the CAP but isn't in the RAP's <code>UserGroupNames</code></td>
<td>A RAP has its own user groups, separate from the CAP (our reading)</td>
<td>Step 10</td>
</tr>
<tr>
<td>Works through one gateway in a farm, fails through another</td>
<td>The RAPs differ between servers, since each gateway processes its own (our reading)</td>
<td>Compare this output on every gateway</td>
</tr>
<tr>
<td>Event 300 for the user and target</td>
<td>The RAP passed</td>
<td>Step 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: Do NPS and the MFA extension answer in time? (read-only)</h2>
<p>Skip this step if <code>CentralCAPEnabled</code> is False and no MFA is in the path. With a central NPS (the usual MFA design), the gateway is a RADIUS client: "Acting as a RADIUS client, the Remote Desktop Gateway server converts the request to a RADIUS Access-Request message and sends the message to the RADIUS (NPS) server where the NPS extension is installed." Read which NPS servers it uses:</p>
<pre><code class="language-powershell"># Read-only. On the RD Gateway: central NPS servers and their order (the class also exposes SharedSecret; don't select it)
Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayRADIUSServer | Select-Object Name, Priority
</code></pre>
<p><code>Priority</code> matters: "The RD Gateway server looks for RD CAPs on RADIUS servers based on the priority." Then open the NPS console on the gateway and read the timeout Microsoft tells you to change: <strong>NPS (Local)</strong> → <strong>RADIUS Clients and Servers</strong> → <strong>Remote RADIUS Server</strong> → <strong>TS GATEWAY SERVER GROUP</strong> → the server → <strong>Edit</strong> → <strong>Load Balancing</strong>. Microsoft: "change the default value from 3 to a value between 30 and 60 seconds". Three seconds isn't enough for a person to find a phone and approve a push.</p>
<p>On the NPS server where the extension is installed:</p>
<pre><code class="language-powershell"># Read-only. On the central NPS server: NPS decisions (6272 granted, 6273 denied) for the last 4 hours
Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 6272, 6273; StartTime = (Get-Date).AddHours(-4) } -MaxEvents 40 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List

# The NPS extension's own logs (AzureMfa): list them, then read the channels it shows (our form)
Get-WinEvent -ListLog '*AzureMfa*' -ErrorAction SilentlyContinue | Select-Object LogName, RecordCount

# Can this server reach the MFA endpoints on TCP 443?
Test-NetConnection -ComputerName login.microsoftonline.com -Port 443
Test-NetConnection -ComputerName strongauthenticationservice.auth.microsoft.com -Port 443
Test-NetConnection -ComputerName adnotifications.windowsazure.com -Port 443
</code></pre>
<pre><code class="language-console">:: Read-only. On the NPS server: is NPS auditing on? (Microsoft's form)
auditpol /get /subcategory:"Network Policy Server"
</code></pre>
<p>Microsoft's NPS checklist says to look for 6273 or 6274: "Most authentication failures produce these events." The extension's logs are under <strong>Applications and Services Logs</strong> → <strong>Microsoft</strong> → <strong>AzureMfa</strong>, and the endpoints come from the extension's network requirements: "The NPS server must be able to communicate with the following URLs over TCP port 443:".</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The push arrives, but the connection has already failed, or only instant approvals work</td>
<td>The gateway's RADIUS timeout is still the default 3 seconds (our reading of Microsoft's instruction to raise it)</td>
<td>Step 10 (30-60 seconds)</td>
</tr>
<tr>
<td>The user only has SMS or a code registered</td>
<td>"The SMS text method doesn't work with Remote Desktop Gateway because it doesn't provide the option to enter a verification code."</td>
<td>Register phone call or Authenticator push</td>
</tr>
<tr>
<td>The user has a TOTP code and push registered, and fails</td>
<td>"If your organization uses Remote Desktop Gateway and the user registered for a TOTP code along with Authenticator push notifications, the user can't meet the Microsoft Entra MFA challenge and Remote Desktop Gateway sign-in fails."</td>
<td>Step 10 (Microsoft's registry override)</td>
</tr>
<tr>
<td>The extension is installed on the gateway itself, or the AzureMfa log shows REQUEST_FORMAT_ERROR</td>
<td>"Don't install the NPS extension on your Remote Desktop Gateway (RDG) server." and "NPS servers that are installed as dependencies for services like RDG and RRAS don't receive radius requests."</td>
<td>Move it to a separate NPS server (Step 10)</td>
</tr>
<tr>
<td>No 6272 or 6273 on the NPS server for the attempt, and an NPS event 13</td>
<td>"A RADIUS message was received from the invalid RADIUS client IP address", so the gateway isn't a RADIUS client there</td>
<td>Add the gateway as a RADIUS client (Step 10)</td>
</tr>
<tr>
<td>NPS event 18</td>
<td>"The NPS event log records this event when authentication fails because the shared secret key of the radius client doesn't match the shared secret key of the NPS server."</td>
<td>Reset the secret on both sides (Step 10)</td>
</tr>
<tr>
<td>6273 on the NPS server</td>
<td>Read the reason code; <a href="https://dev.to/adminpackstudio/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage-26i6">NPS Event 6273 Reason Code 16 or 22</a> maps them. Microsoft's RD Gateway guide: "Ensure that your new policy is at the top of the list, that the policy is enabled, and that it grants access."</td>
<td>Fix the network policy order or conditions</td>
</tr>
<tr>
<td>AzureMfa log: AccessReject</td>
<td>"This error usually reflects an authentication failure in AD or that the NPS server is unable to receive responses from Microsoft Entra ID."</td>
<td>The AD account first, then the endpoints above</td>
</tr>
<tr>
<td>AzureMfa log: InvalidSession</td>
<td>"The session has taken more than three minutes to complete."</td>
<td>The user's response time, or latency on the path</td>
</tr>
<tr>
<td>A <code>Test-NetConnection</code> above fails</td>
<td>Outbound 443 from the NPS server is blocked or proxied</td>
<td>Allow the endpoints from that server</td>
</tr>
<tr>
<td>Prompts are sent, but users never see them</td>
<td>The phone side; <a href="https://dev.to/adminpackstudio/microsoft-authenticator-not-receiving-notifications-a-read-first-sign-in-log-device-and-policy-ffn">Microsoft Authenticator Not Receiving Notifications</a> covers it</td>
<td>That post</td>
</tr>
</tbody></table>
<p>Discarded duplicate requests in the NPS log are expected while a user is still answering the prompt: "Discarded requests in the NPS server event log don't indicate there's a problem with the NPS server or the Microsoft Entra multifactor authentication NPS extension."</p>
<hr />
<h2>Step 8: Can the gateway reach the target, and does the target accept the user? (read-only)</h2>
<p>Once events 200 and 300 are logged, the gateway's job is to open RDP to the target from inside. Microsoft's ports page lists that leg: "TCP and UDP 3389: Used by RDP." From the gateway:</p>
<pre><code class="language-powershell"># Read-only. From the RD Gateway: does the target resolve, and does it answer on 3389, by the name users type? (names are examples)
Resolve-DnsName -Name app01.contoso.com -Type A
Test-NetConnection -ComputerName app01.contoso.com -Port 3389
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Event 304</td>
<td>"The remote client met the conditions specified in the TS CAP and the TS RAP, but could not connect to the internal network resource (computer) through the TS Gateway server because the remote computer was unavailable."</td>
<td>The tests above, then the target</td>
</tr>
<tr>
<td>The name doesn't resolve from the gateway</td>
<td>The gateway needs internal DNS: "TCP and UDP 53: Used by the Domain Name System (DNS) for internal resource name resolution."</td>
<td>The gateway's DNS client settings</td>
</tr>
<tr>
<td>3389 fails from the gateway, works from a PC on the same subnet as the target</td>
<td>A firewall between the gateway's subnet and the target (our reading)</td>
<td>Allow 3389 from the gateway</td>
</tr>
<tr>
<td>3389 fails from everywhere</td>
<td>The target's RDP listener or service. Microsoft: "On both the local (client) computer and the remote (target) computer, the RDP listener should be listening on port 3389."</td>
<td>The target</td>
</tr>
<tr>
<td>Event 302 then 303 within seconds</td>
<td>The session reached the target and ended there (our reading); a licensing message points at <a href="https://dev.to/adminpackstudio/remote-desktop-licensing-mode-is-not-configured-a-read-first-license-server-cal-and-grace-period-2j3l">Remote Desktop Licensing Mode Is Not Configured</a></td>
<td>The session host's own logs</td>
</tr>
<tr>
<td>"Access is denied" after the session starts</td>
<td>"This error usually indicates that the user who is trying to connect doesn't have the necessary permission to access the remote server." For a domain controller target: "Only members of the Domain Admins group can use RDP to connect to a domain controller."</td>
<td>Remote Desktop Users or the collection's user groups</td>
</tr>
<tr>
<td>"Account restriction is preventing this user from signing in"</td>
<td>Credential Guard: "It's not supported for connections that use RD Connection Broker or RD Gateway."</td>
<td>Microsoft's linked Credential Guard guidance</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Is UDP 3391 working, and why is RD Gateway Manager empty? (read-only)</h2>
<p>Read the live connections on the gateway. This is the same kind of list RD Gateway Manager's <strong>Monitoring</strong> node shows (our reading):</p>
<pre><code class="language-powershell"># Read-only. On EACH RD Gateway: live connections and their transport (0 = RPC over HTTP, 1 = HTTP, 2 = UDP)
Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayConnection |
  Select-Object UserName, ClientAddress, ConnectedResource, ConnectedPort, TransportProtocol, ConnectedTime, ConnectionDuration | Format-Table -AutoSize
</code></pre>
<p>UDP is the second transport: "UDP 3391: Used for RDP over UDP." Microsoft also notes that firewalls doing directional UDP analysis need the UDP rule set to Send Receive. Step 4 already showed whether transport 2 is enabled and listening.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>UDP enabled and listening on 3391, every live connection shows <code>TransportProtocol</code> 1</td>
<td>UDP isn't making it through the edge or a load balancer, and sessions run on HTTP only (our reading)</td>
<td>Allow UDP 3391 both ways (Step 10)</td>
</tr>
<tr>
<td>A load balancer in front, HTTPS rule only</td>
<td>Microsoft's high-availability steps create a separate UDP rule for 3391 next to the 443 rule</td>
<td>Add the UDP rule</td>
</tr>
<tr>
<td>Some connections show 2</td>
<td>UDP works for those clients</td>
<td>Compare client networks</td>
</tr>
<tr>
<td>Nothing listed here or in Monitoring, but users say they're connected</td>
<td>They aren't going through this gateway: another server in the farm, <code>Gatewaymode</code> DoNotUse, or local bypass (Step 2) (our reading)</td>
<td>Run this on every gateway, then Step 2</td>
</tr>
<tr>
<td>Monitoring was open before the user connected</td>
<td>It doesn't update on its own. Microsoft: "To refresh the display of connection status, in the Actions pane, click Refresh."</td>
<td>Refresh</td>
</tr>
<tr>
<td><code>ClientAddress</code> is the same address for everyone</td>
<td>"If your network configuration includes proxy servers, the IP address that appears in this column will reflect the IP address of the proxy server, rather than the IP address of the Terminal Services client."</td>
<td>Expected behind a proxy or NAT</td>
</tr>
<tr>
<td>Connections show here, but no 200/300/302 events</td>
<td>Those events aren't being logged. In RD Gateway Manager, the server's <strong>Properties</strong> → <strong>Auditing</strong> tab is where "you can specify the types of events that you want to monitor" (Microsoft's TS Gateway wording)</td>
<td>Turn the event types back on</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>What people try</th>
<th>Why it misleads (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Reinstalling the RD Gateway role</td>
<td>It removes the CAPs, RAPs, resource groups, NPS settings, and certificate binding you needed to read, and the cause (a name, a certificate, a policy, a firewall) is usually still there</td>
</tr>
<tr>
<td>Testing from inside the office</td>
<td>Inside clients may resolve a different address or bypass the gateway (Step 2)</td>
</tr>
<tr>
<td>Importing the renewed certificate and stopping there</td>
<td>The gateway may still bind and serve the old one; compare <code>CertHash</code>, <code>netsh http show sslcert</code>, and the thumbprint (Step 3)</td>
</tr>
<tr>
<td>Turning off the Windows Firewall on the gateway</td>
<td>The edge firewall or NAT in front is the usual block for outside users; this opens the server and proves little</td>
</tr>
<tr>
<td>Changing the CAP and RAP to allow everyone to everything</td>
<td>The gateway becomes an open path from the internet to every internal computer; events 201 and 301 already say which policy and which name failed</td>
</tr>
<tr>
<td>Uninstalling the NPS extension</td>
<td>MFA is gone, and if CAPs are central the RADIUS timeout or method problem is still there (Step 7)</td>
</tr>
<tr>
<td>Installing the NPS extension on the gateway</td>
<td>Microsoft: "Don't install the NPS extension on your Remote Desktop Gateway (RDG) server."</td>
</tr>
<tr>
<td><code>Test-NetConnection</code> to 3391</td>
<td>It tests TCP only; 3391 is UDP</td>
</tr>
<tr>
<td>Blaming UDP for a connection that never starts</td>
<td>UDP is a second transport; a session that never starts is usually Steps 2-8 (our reading)</td>
</tr>
<tr>
<td>Restarting TSGateway as the fix</td>
<td>It can clear a stuck state, and it also hides which link failed; read the log first</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one link, then prove it</h2>
<p>Only after Steps 1-9 agree on which link failed. Save the gateway's configuration first. The <code>Export</code> method "Returns the Remote Desktop Gateway (RD Gateway) server configuration as an XML string.", and its export bits add up to everything that matters here:</p>
<pre><code class="language-powershell"># Read-only, but store the file like a secret. On EACH RD Gateway: export CAPs (1), RADIUS servers (2), RAPs (4), resource groups (8), load-balancing servers (16), and server settings (32) = 63 (our form; path is an example)
$srv = Get-CimInstance -Namespace root/CIMV2/TerminalServices -ClassName Win32_TSGatewayServer
$out = Invoke-CimMethod -InputObject $srv -MethodName Export -Arguments @{ ExportType = [uint32]63 }
$out.XmlString | Out-File -FilePath C:\Temp\rdg-before.xml -Encoding utf8
</code></pre>
<p>Keep the Step 3 thumbprints, the Step 5 and 6 policy output, and the NPS timeout values with it (our habit).</p>
<p><strong>The public name or the edge (Step 2).</strong> Fix the public DNS record, or the edge rule and NAT to the gateway: TCP 443 inbound, and UDP 3391 if you use UDP, with the UDP rule allowing both directions. If RD Web launches hand out the wrong name, set it on the RD Connection Broker (names are examples):</p>
<pre><code class="language-powershell"># Change. On the RD Connection Broker: the external gateway name clients get from the deployment
Set-RDDeploymentGatewayConfiguration -GatewayMode Custom -GatewayExternalFqdn rdg.contoso.com -LogonMethod Password -UseCachedCredentials $true -BypassLocal $false -ConnectionBroker rdcb.contoso.com
</code></pre>
<p><code>Custom</code> means "Remote users get Gateway settings from other parameters to this cmdlet." Setting <code>-BypassLocal $false</code> also makes inside tests go through the gateway, which you want while you troubleshoot (our reading).</p>
<p><strong>The certificate (Step 3).</strong> Get a certificate whose name matches the public FQDN, with Server Authentication, from a CA your outside clients trust (Microsoft recommends public for the gateway: "Because the RD Gateway role is supposed to be public, we recommend you use a publicly issued certificate."). In a deployment, assign it to the RD Gateway role on the RD Connection Broker with Microsoft's form:</p>
<pre><code class="language-powershell"># Change. On the RD Connection Broker: assign the PFX to the RD Gateway role (Microsoft's form with Role RDGateway; path is an example)
$password = Read-Host -AsSecureString -Prompt "Enter Password"
Set-RDCertificate -Role RDGateway -ImportPath 'C:\Certs\rdg-2026.pfx' -Password $password -ConnectionBroker rdcb.contoso.com
</code></pre>
<p>On Windows Server 2019 and later, <code>-Thumbprint</code> can point at a certificate that's already installed ("The parameter is available on Windows Server 2019 and later."). For a standalone gateway, use RD Gateway Manager: the server's <strong>Properties</strong> → <strong>SSL Certificate</strong> → <strong>Import a certificate into the RD Gateway</strong> → <strong>Browse and Import Certificate</strong>. Microsoft's high-availability steps add a note worth keeping: "You may need to manually restart the TSGateway service running on each RD Gateway server, either through Server Manager or Task Manager." Then rerun Step 3 on the gateway and on an outside client. If outside clients can't reach your CRL, publish it where they can before you move on.</p>
<p><strong>The service or limits (Step 4).</strong> Start TSGateway and read why it stopped. Raise <code>MaxConnections</code> only if the limit, not an attack, was the cause (in RD Gateway Manager, server <strong>Properties</strong>).</p>
<p><strong>The CAP (Step 5).</strong> Add the user to the group the CAP already names, or add the right group to that CAP in RD Gateway Manager (<strong>Policies</strong> → <strong>Connection Authorization Policies</strong>). Don't widen it to Domain Users or remove the computer group condition unless you've decided those devices should connect.</p>
<p><strong>The RAP (Step 6).</strong> Add the name users actually type (short name and FQDN) to the RD Gateway-managed computer group, or the computer to the AD group the RAP uses, or the custom port to the RAP. Do it on every gateway in the farm.</p>
<p><strong>NPS and MFA (Step 7).</strong> Raise the gateway's RADIUS timeout to 30-60 seconds on the <strong>Load Balancing</strong> tab, and set "Number of seconds between requests when server is identified as unavailable" to the same or more (Microsoft's step). If the NPS server doesn't know the gateway, add it as a RADIUS client with the same shared secret; if event 18 shows a secret mismatch, set a new long secret on both sides. If the NPS server isn't registered, Microsoft's step is <strong>Register server in Active Directory</strong>: "To function properly in this scenario, the NPS server needs to be registered in Active Directory." For users with both TOTP and push registered, Microsoft's fix is a registry value under <code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\AzureMfa</code> on the NPS extension server, then an NPS service restart:</p>
<pre><code class="language-powershell"># Change. On the NPS EXTENSION server (not the gateway): fall back to Approve/Deny for RD Gateway (Microsoft's value, string FALSE)
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\AzureMfa' -Name OVERRIDE_NUMBER_MATCHING_WITH_OTP -Value 'FALSE' -PropertyType String -Force
Restart-Service -Name IAS
</code></pre>
<p>Microsoft's wording: "set OVERRIDE_NUMBER_MATCHING_WITH_OTP = FALSE to fall back to Approve/Deny push notifications with Authenticator." If the extension sits on the gateway, install it on a separate NPS server and point the gateway's central CAP store there, following Microsoft's RD Gateway integration steps, instead of patching it in place.</p>
<p><strong>The target (Step 8).</strong> Fix the firewall from the gateway to the target, the target's RDP listener, or the user's rights on the target. Nothing on the gateway changes.</p>
<p><strong>UDP (Step 9).</strong> Allow UDP 3391 both ways at the edge and on any load balancer. If you can't yet, sessions keep working over HTTP; leave UDP enabled on the gateway so it starts working when the edge is fixed (our reading).</p>
<p><strong>Then prove it.</strong> From an outside client, as the user who failed: the connection succeeds, the gateway log shows 200, 300, and 302 for that user at that time, <code>Win32_TSGatewayConnection</code> lists the session (with <code>TransportProtocol</code> 2 if UDP should work), <code>Test-Certificate -DNSName</code> returns True for the public name, and NPS logs 6272 when MFA is in the path (our reading). Then have the helpdesk retest the original users.</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Public name or edge rule wrong</td>
<td>Fix the record or the NAT and edge rule for 443 (and 3391 UDP)</td>
<td>Opening 3389 to the internet instead</td>
</tr>
<tr>
<td>Certificate old, expired, mismatched, or private</td>
<td>A public certificate with the right name, bound on every gateway</td>
<td>Telling users to click through the warning</td>
</tr>
<tr>
<td>CAP fails for a user</td>
<td>Add the user to the group the CAP names</td>
<td>A CAP for Domain Users with no other conditions</td>
</tr>
<tr>
<td>RAP fails by short name</td>
<td>Add the names users type, or teach the FQDN</td>
<td>A RAP that allows any resource</td>
</tr>
<tr>
<td>MFA times out</td>
<td>RADIUS timeout 30-60 seconds</td>
<td>Removing MFA or the NPS extension</td>
</tr>
<tr>
<td>TOTP plus push users fail</td>
<td>Microsoft's <code>OVERRIDE_NUMBER_MATCHING_WITH_OTP = FALSE</code></td>
<td>Deleting the users' TOTP method without telling them</td>
</tr>
<tr>
<td>304 to one target</td>
<td>The target or the firewall in front of it</td>
<td>Changing gateway policies</td>
</tr>
<tr>
<td>UDP never used</td>
<td>UDP 3391 open both ways on the edge and the load balancer</td>
<td>Disabling UDP on the gateway to make the symptom go away</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why is my RD Gateway not working externally when it works inside?</strong>
Usually the public name points somewhere else, the edge firewall or NAT doesn't pass TCP 443 to the gateway, or inside clients bypass the gateway so the inside test proves nothing. Microsoft requires "inbound TCP connections to port 443 and inbound UDP connections to port 3391" on the public address. Test <code>Resolve-DnsName</code> and <code>Test-NetConnection -Port 443</code> from an outside network (Step 2).</p>
<p><strong>What does "Remote Desktop Gateway server is temporarily unavailable" mean?</strong>
The client didn't get a usable answer from the gateway: the name, 443, the TLS handshake, the service, or (with MFA) a RADIUS timeout. For Windows 365 Boot, Microsoft says "This issue might be a transient issue because of network congestion.", but for your own gateway, read Steps 2-4 and 7 before you wait it out.</p>
<p><strong>What ports does RD Gateway use?</strong>
From clients: TCP 443 for HTTPS and UDP 3391 for "RDP over UDP". From the gateway inward: 3389 to the targets, DNS, Kerberos and LDAP to AD, CRL ports, and "UDP 1812 and 1813: Required when Network Policy Server (NPS) is used."</p>
<p><strong>How do I fix an expired RD Gateway certificate?</strong>
Get a new certificate with the public name and Server Authentication, assign it to the RD Gateway role (<code>Set-RDCertificate -Role RDGateway</code>, or RD Gateway Manager's <strong>SSL Certificate</strong> tab), restart TSGateway if needed, and confirm <code>CertHash</code> and <code>netsh http show sslcert</code> show the new thumbprint (Steps 3 and 10).</p>
<p><strong>Why isn't RD Gateway MFA with the NPS extension prompting or working?</strong>
Most often the gateway's RADIUS timeout is still 3 seconds, the user only has SMS or a code (RD Gateway supports phone call and Authenticator Approve/Deny), the user has both TOTP and push registered, or the extension is installed on the gateway. Read the AzureMfa log and Security events 6272/6273 on the NPS server (Step 7).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage-26i6">NPS Event 6273 Reason Code 16 or 22</a> (when the central NPS denies the gateway's RADIUS request)</li>
<li><a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline 0x80092013</a> (when outside clients can't check the gateway certificate's CRL)</li>
<li><a href="https://dev.to/adminpackstudio/microsoft-authenticator-not-receiving-notifications-a-read-first-sign-in-log-device-and-policy-ffn">Microsoft Authenticator Not Receiving Notifications</a> (when the MFA push is sent but never shows up)</li>
<li><a href="https://dev.to/adminpackstudio/remote-desktop-licensing-mode-is-not-configured-a-read-first-license-server-cal-and-grace-period-2j3l">Remote Desktop Licensing Mode Is Not Configured</a> (when the session host refuses users after the gateway lets them through)</li>
<li><a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> (when an internal CA should be renewing the gateway or NPS certificates)</li>
</ul>
<h2>Free checklist</h2>
<p>Reinstalling the gateway and opening every policy before anyone read events 201 and 301 is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=rd-gateway-not-working">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=rd-gateway-not-working</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=rd-gateway-not-working">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=rd-gateway-not-working</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>Resolve-DnsName</code>, <code>Test-NetConnection</code>, <code>Get-RDDeploymentGatewayConfiguration</code>, <code>netsh http show sslcert</code>, <code>Get-ChildItem Cert:</code>, <code>Test-Certificate</code>, <code>Get-RDCertificate</code>, <code>certutil -urlfetch -verify</code>, <code>Get-Service</code>, <code>Get-NetTCPConnection</code>, <code>Get-NetUDPEndpoint</code>, <code>Get-CimInstance</code> and read-only <code>Invoke-CimMethod</code> calls on the RD Gateway WMI classes, <code>Get-WinEvent</code>, and <code>auditpol /get</code>. Exporting the gateway configuration creates a sensitive file. Changing DNS, edge rules, certificates, gateway settings, CAPs, RAPs, resource groups, RADIUS timeouts or secrets, NPS registration, or the NPS extension registry, and restarting services, are deliberate changes. Change one link at a time, test from an outside client and from the gateway, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Windows DNS Server Not Resolving External Names: A Read-First Forwarder, Root Hints, and Recursion Triage]]></title><description><![CDATA[Windows DNS server not resolving external names? Read recursion, each forwarder and its timeout, root hints, and conditional forwarders first.]]></description><link>https://adminpackstudio.hashnode.dev/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/windows-dns-server-not-resolving-external-names-a-read-first-forwarder-root-hints-and-recursion-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[dns]]></category><category><![CDATA[networking]]></category><category><![CDATA[sysadmin]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 04:43:22 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since the firewall cutover on Friday, intranet sites open fine but external sites fail or take ten seconds, and nslookup on a PC says 'DNS request timed out' and then works on the second try. The Dallas office also can't reach the partner portal at portal.fabrikam.com, but head office can. A forum post says to point every PC at a public resolver, add five more public DNS servers as forwarders on the DCs, and restart the DNS Server service on all of them. Can we do that?"</p>
<p>Not yet. <strong>"DNS not resolving external names" is almost never the client. Your domain controllers answer internal names from their own zones; external names depend on a chain the client never sees: recursion allowed on the server, a forwarder that answers inside the timeout, root hints the server can actually reach as a fallback, a conditional forwarder that exists on the DC the client asked, and a network that passes DNS over UDP and TCP 53.</strong> Microsoft describes the end of that chain plainly: "If the server configuration disables recursion, the server normally returns REFUSED for a query that requires recursion. If recursive processing can't complete, the server returns SERVFAIL." And the part most fixes miss: "An unresponsive forwarder doesn't guarantee that the server reaches root hint fallback." Pointing PCs at a public resolver breaks Active Directory lookups, five more forwarders can't all be reached in the default timeout, and a service restart clears the cache and the forwarder state you need to read. Work through it as a chain: <strong>which client, which DNS server, which name, which error</strong> → <strong>is the client asking the server you think?</strong> → <strong>does the server resolve it itself, and is recursion allowed?</strong> → <strong>which path does the server pick for this name?</strong> → <strong>do the forwarders answer, inside the timeout?</strong> → <strong>can root hints fallback actually happen?</strong> → <strong>does the conditional forwarder exist on this DC, and do its targets answer?</strong> → <strong>is a cached answer hiding the fix?</strong> → <strong>is the network dropping large or TCP DNS?</strong> <code>Resolve-DnsName</code>, <code>Get-DnsServerForwarder</code>, <code>Get-DnsServerRecursion</code>, <code>Get-DnsServerZone</code>, <code>Get-DnsServerRootHint</code>, <code>Test-DnsServer</code>, <code>Show-DnsServerCache</code>, and <code>nslookup</code> are the sources for that chain. Change the forwarders first and you'll never know which one was dead.</p>
<p>If external lookups fail from domain controllers at boot and Netlogon logs 5719, <a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> covers the DC locator side. If records go missing from your internal zones, <a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers aging. This post covers <strong>"a Windows DNS server (usually a domain controller) resolves internal names but not internet names, resolves them slowly or only on retry, or can't resolve one partner or cloud namespace through a conditional forwarder, and you need a read-first path before you change client DNS settings, forwarders, root hints, timeouts, or recursion."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Prove the server, not the client.</strong> Microsoft: "Check whether the resolution fails from the DNS server itself. If so, you can rule out any issue with the client or the network between the client and the DNS server." (Step 3).</li>
<li><strong>The server picks one path per name.</strong> Hosted zone or cache first, then a matching conditional forwarder, a delegation, the server-level forwarders, and root hints last (Step 4).</li>
<li><strong>Dead forwarders cost seconds each.</strong> Microsoft: "with default settings, a Windows DNS server will be able to query at most three forwarders." (Step 5).</li>
<li><strong>Root hints are a fallback only if the clock and the firewall allow it.</strong> Microsoft: "If the overall recursion timeout expires first, the query fails without reaching the root hints." (Step 6).</li>
<li><strong>Conditional forwarders are zones, and zones have a replication scope.</strong> One stored in the registry of DC1 doesn't exist on DC2 (Step 7).</li>
<li><strong>A negative answer can outlive the fix.</strong> Clear the cache once, after the fix, not as the fix (Step 8).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which client, which DNS server, which name, which error, since when</td>
<td>"DNS is down" with no server or name written down</td>
</tr>
<tr>
<td>2. Client points at the right server</td>
<td>The client asks your internal DNS server, and gets the same answer you get</td>
<td>Works from your PC, fails from theirs; nslookup works, the browser doesn't</td>
</tr>
<tr>
<td>3. Server resolves, recursion allowed</td>
<td>The DNS server resolves the name when asked directly</td>
<td>"Query refused", or failure from the DC itself</td>
</tr>
<tr>
<td>4. Resolution path</td>
<td>You know whether a zone, conditional forwarder, delegation, forwarder, or root hint handles the name</td>
<td>An internal zone answers for an external name</td>
</tr>
<tr>
<td>5. Forwarders</td>
<td>Each forwarder answers fast enough, and the list fits the timeout</td>
<td>"Server failure" after 8-12 seconds, slow first lookups</td>
</tr>
<tr>
<td>6. Root hints fallback</td>
<td>Root hints are present, allowed, and reachable</td>
<td>Forwarders down and nothing falls back</td>
</tr>
<tr>
<td>7. Conditional forwarder</td>
<td>It exists on every DC that needs it, and its targets answer</td>
<td>One partner domain fails from one site</td>
</tr>
<tr>
<td>8. Cache</td>
<td>No stale negative answer on the server or the client</td>
<td>Works after clearing the cache, breaks again later</td>
</tr>
<tr>
<td>9. Network</td>
<td>UDP and TCP 53 pass both ways, including large responses</td>
<td>Most names work, a few domains never do</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one link, one change</td>
<td>Five new forwarders, same failure</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which client</strong></td>
<td>One PC, one site, one VLAN, every client, or the DNS server itself. Microsoft's first questions are "Check whether all clients are facing the issue or only specific ones are." and whether the failing names are internal or external</td>
</tr>
<tr>
<td><strong>Which DNS server it asked</strong></td>
<td>The IP address in the client's DNS settings, not the one you assume. A site may use its own DC (Step 2)</td>
</tr>
<tr>
<td><strong>Which name and record type</strong></td>
<td>An internet name, a partner or cloud namespace (Step 7), or a name inside your own zone name (Step 4)</td>
</tr>
<tr>
<td><strong>Exact error text</strong></td>
<td>nslookup's "timed out", "Server failure", "Refused", or "Nonexistent domain", or <code>Resolve-DnsName</code>'s error. Each points at a different link</td>
</tr>
<tr>
<td><strong>Slow or failed</strong></td>
<td>A 3-12 second delay that eventually works is a timeout pattern (Step 5); an instant failure is an answer (Steps 3, 4, 7)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>A firewall or ISP change, a new public IP, a new forwarder, a new DC, a DNS policy, a Windows Server upgrade</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Internal names also fail, or every query to the DC times out</td>
<td>The client doesn't reach that DNS server, or the DNS service isn't answering</td>
<td>Steps 2 and 3</td>
</tr>
<tr>
<td>nslookup works, ping or the browser fails</td>
<td>The client's cache, hosts file, or suffix search, not the server</td>
<td>Step 2</td>
</tr>
<tr>
<td>"Refused" or "Query refused"</td>
<td>Recursion is disabled, or a DNS policy denies the query</td>
<td>Step 3</td>
</tr>
<tr>
<td>An external name under your internal domain name fails</td>
<td>Your internal zone answers for it authoritatively</td>
<td>Step 4</td>
</tr>
<tr>
<td>External names fail with "Server failure" after several seconds, or work on retry</td>
<td>A forwarder is dead or slow, and the timeout runs out</td>
<td>Step 5</td>
</tr>
<tr>
<td>Forwarders are unreachable and nothing falls back</td>
<td>Root hints disabled, empty, or blocked</td>
<td>Step 6</td>
</tr>
<tr>
<td>One partner or cloud namespace fails, the internet is fine</td>
<td>The conditional forwarder, its replication scope, or its targets</td>
<td>Step 7</td>
</tr>
<tr>
<td>Works on DC1, fails on DC2</td>
<td>Different forwarders, a conditional forwarder missing on DC2</td>
<td>Steps 5 and 7</td>
</tr>
<tr>
<td>Fails, works after clearing the server cache, fails again later</td>
<td>A negative answer cached on the server</td>
<td>Step 8</td>
</tr>
<tr>
<td>Most names work, a few specific domains never resolve</td>
<td>Large UDP responses or TCP 53 dropped</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Is the client asking the server you think? (read-only)</h2>
<p>Microsoft's client checklist starts with the configuration: run <code>ipconfig /all</code> and "Check the DNS servers that are listed in the output, and verify that the IP addresses listed are correct." On the failing client:</p>
<pre><code class="language-powershell"># Read-only. On the failing client: which DNS servers does each adapter use?
Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object { $_.ServerAddresses } |
  Select-Object InterfaceAlias, ServerAddresses | Format-Table -AutoSize

# Ask each internal DNS server directly, DNS protocol only, hosts file skipped (addresses are examples)
Resolve-DnsName -Name www.microsoft.com -Server 10.0.0.10 -DnsOnly -NoHostsFile
Resolve-DnsName -Name www.microsoft.com -Server 10.20.0.10 -DnsOnly -NoHostsFile

# What the client has cached for the name right now
Get-DnsClientCache -Entry www.microsoft.com -ErrorAction SilentlyContinue
</code></pre>
<p><code>-Server</code> is documented as "Specifies the IP addresses or host names of the DNS servers to be queried." and without it "the interface DNS servers are queried". So a lookup that works with <code>-Server</code> and fails without it says the client is asking someone else.</p>
<p>Why nslookup and the browser disagree: Microsoft notes that "Because nslookup doesn't use the client's DNS cache, name resolution will use the client's configured DNS server." Ping and browsers go through the DNS Client service, which checks its cache first, and that cache is preloaded from the hosts file: "If a Hosts file is configured locally, any host name-to-address mappings from that file are loaded into the cache when the DNS Client service is started."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The client lists a router, a public resolver, or an old DC</td>
<td>It isn't asking your internal DNS server. Microsoft: "Run ipconfig /all on the client and confirm the correct DNS server IP is configured."</td>
<td>Fix the DHCP scope option or static setting (Step 10); for DHCP, <a href="https://dev.to/adminpackstudio/windows-server-dhcp-not-assigning-ip-addresses-a-read-first-scope-authorization-relay-and-d13">Windows Server DHCP Not Assigning IP Addresses</a> covers scope options</td>
</tr>
<tr>
<td><code>Resolve-DnsName -Server</code> works for one DC and fails for another</td>
<td>The DNS servers differ</td>
<td>Step 3 on the failing one</td>
</tr>
<tr>
<td>Every query to the DC times out, internal names too</td>
<td>The client doesn't reach the DNS service. Microsoft: "Ensure that UDP port 53 communication between the client and the DNS server is allowed."</td>
<td>Step 3 (service, listening address), then the network path</td>
</tr>
<tr>
<td>nslookup works, ping or the browser fails</td>
<td>A hosts file entry, a cached negative answer, or a short name with the wrong suffix</td>
<td>Read the cache and hosts file; Step 8 for the cache</td>
</tr>
<tr>
<td>The same failure from every client and from the DNS server itself</td>
<td>Server side</td>
<td>Step 3</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Does the DNS server resolve it itself, and is recursion allowed? (read-only)</h2>
<p>Microsoft's server checklist runs the same lookup against the server's own address: <code>nslookup &lt;name&gt; &lt;IP address of the DNS server&gt;</code>, and "If you get a failure or time-out response, see Checking for recursion problems." On the DNS server:</p>
<pre><code class="language-powershell"># Read-only. On the DNS server: resolve through itself, on the address clients use (example address)
Resolve-DnsName -Name www.microsoft.com -Server 10.0.0.10 -DnsOnly -NoHostsFile
Get-Service -Name DNS

# Recursion: the server-wide setting, recursion scopes, and any query resolution policies
Get-DnsServerRecursion
Get-DnsServerRecursionScope
Get-DnsServerQueryResolutionPolicy | Format-List *

# Which addresses the service listens on
Get-DnsServerSetting -All | Select-Object ListeningIPAddress, AllIPAddress
</code></pre>
<pre><code class="language-powershell"># Read-only. On the DNS server: recent errors and warnings in the DNS Server log
Get-WinEvent -FilterHashtable @{ LogName = 'DNS Server'; Level = 2, 3; StartTime = (Get-Date).AddDays(-2) } -MaxEvents 30 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>Microsoft's recursion cmdlet defines the term: "Recursion occurs when a DNS server queries other DNS servers on behalf of a requesting client, and then sends the answer back to the client." Recursion scopes are the newer layer on top: "The legacy recursion setting and list of forwarders are referred to as the default recursion scope." That default scope is named with a single dot, and a policy can send some queries to a different scope.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>nslookup returns Refused, and <code>Get-DnsServerRecursion</code> shows recursion disabled</td>
<td>"If the server configuration disables recursion, the server normally returns REFUSED for a query that requires recursion."</td>
<td>Step 10 (only on an internal-only server)</td>
</tr>
<tr>
<td>The dot scope has <code>EnableRecursion</code> False, and a policy points internal clients at another scope</td>
<td>Selective recursion. Queries that don't match the policy (a new subnet, a new interface) get the default: no recursion (our reading)</td>
<td>Read the policy criteria against the client's subnet and the server interface</td>
</tr>
<tr>
<td>A policy with a Deny action matches the name or record type</td>
<td>The server refuses on purpose</td>
<td>Find who added it before removing it</td>
</tr>
<tr>
<td>The DNS service isn't running</td>
<td>Microsoft's forwarder article: "Ensure the DNS server service is running on both the forwarding and forwarder DNS servers"</td>
<td>Start it, then read the DNS Server log for why it stopped</td>
</tr>
<tr>
<td>The address clients use isn't in <code>ListeningIPAddress</code></td>
<td>"the server might not be listening on the IP address that you used in your nslookup query."</td>
<td>Step 10 (Interfaces tab)</td>
</tr>
<tr>
<td>Resolves fine on the server, clients fail</td>
<td>The path between client and server</td>
<td>Back to Step 2</td>
</tr>
<tr>
<td>Fails on the server too, with a delay or "Server failure"</td>
<td>A recursion problem</td>
<td>Step 4</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Which path does the server pick for this name? (read-only)</h2>
<p>This is the step most fixes skip. Microsoft's current forwarding page lists the order a Windows DNS server follows for a recursive query (with the default <code>ForwardDelegations</code> off): local zones and cache, then "Use a matching conditional forwarder.", then "Use an applicable delegation.", then "Use server-level forwarders.", then "Use root hints when the configuration permits." A zone the server hosts wins outright: "If a hosted authoritative zone covers the query, that zone determines the result."</p>
<pre><code class="language-powershell"># Read-only. On the DNS server: which hosted zone, conditional forwarder, or root zone matches the failing name? (our form)
$name = 'portal.fabrikam.com'
Get-DnsServerZone | Where-Object { $name -eq $_.ZoneName -or $name -like "*.$($_.ZoneName)" -or $_.ZoneName -eq '.' } |
  Select-Object ZoneName, ZoneType, IsDsIntegrated, IsAutoCreated | Format-Table -AutoSize
Get-DnsServerSetting -All | Select-Object ForwardDelegations
</code></pre>
<p>Microsoft's forwarder article starts the same way: <code>Get-DnsServerZone -Name &lt;domainname&gt;</code> to check for a conditional forwarder, then <code>Get-DnsServerForwarder</code>, then <code>Get-DnsServerRootHint</code>. "If none of them are configured, the name resolution is expected to fail."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>A zone named <code>.</code> (dot) with type Primary</td>
<td>The server thinks it's a root server and won't forward or use root hints. Microsoft's DC guidance says that to forward to the internet "you also must delete the root" zone</td>
<td>Step 10</td>
</tr>
<tr>
<td>A Primary or Secondary zone matches the name (for example your internal <code>contoso.com</code> and the failing name is <code>www.contoso.com</code>)</td>
<td>The server answers authoritatively, and a missing record is a final answer: "It doesn't send that query to a conditional forwarder, server-level forwarder, or root hints."</td>
<td>Add the record to the internal zone (Step 10)</td>
</tr>
<tr>
<td>A zone with type Forwarder matches</td>
<td>A conditional forwarder controls this name</td>
<td>Step 7</td>
</tr>
<tr>
<td>A delegation inside a hosted zone matches</td>
<td>The delegated name servers must answer. Microsoft's broken-delegation test walks it with <code>nslookup</code>, <code>set norecursion</code>, and the delegated servers</td>
<td>Fix the delegation's NS and glue records</td>
</tr>
<tr>
<td>Nothing matches</td>
<td>Server-level forwarders, then root hints</td>
<td>Steps 5 and 6</td>
</tr>
<tr>
<td><code>ForwardDelegations</code> is True</td>
<td>The order above may not apply. Microsoft: "If your environment enables that setting, validate the resulting interaction between delegations and forwarders before relying on this resolution order."</td>
<td>Read it before going further</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Do the forwarders answer, inside the timeout? (read-only)</h2>
<p>Read the forwarder list and the two timers that decide how many of them the server ever tries:</p>
<pre><code class="language-powershell"># Read-only. On the DNS server: forwarder list, per-forwarder timeout, root-hint fallback, dynamic reordering
Get-DnsServerForwarder
# Overall recursion budget (Timeout) and whether recursion is on
Get-DnsServerRecursion
</code></pre>
<p>Then ask each forwarder directly from the DNS server, and time it. This is the test the server itself is making:</p>
<pre><code class="language-powershell"># Read-only. From the DNS server: does each forwarder answer, and how fast? (our form)
foreach ($fwd in (Get-DnsServerForwarder).IPAddress) {
  $ip = "$fwd"
  $sw = [System.Diagnostics.Stopwatch]::StartNew()
  $ok = [bool](Resolve-DnsName -Name www.microsoft.com -Server $ip -DnsOnly -QuickTimeout -ErrorAction SilentlyContinue)
  $sw.Stop()
  [pscustomobject]@{ Forwarder = $ip; Answered = $ok; Milliseconds = $sw.ElapsedMilliseconds }
}
# Microsoft's built-in check of the configured forwarders (the address is the DNS server's own)
Test-DnsServer -IPAddress 10.0.0.10 -Context Forwarder
</code></pre>
<p>How the timers interact, from Microsoft's timeout article (KB 2834250 in the old numbering): the server queries the first forwarder at 0 seconds, the second "after 3.5 seconds", the third "after 3.5 + 4 = 7.5 seconds", and then "At the eighth second, RecursionTimeout expires". The client gets its answer late: "We'll send the Server Failure response then after 11.5 seconds." Two Microsoft pages disagree on the default per-forwarder timeout (the timeout article says 3 seconds, the <code>Set-DnsServerForwarder</code> reference says "The default value is 5."), so read the value <code>Get-DnsServerForwarder</code> returns rather than trusting either.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>One forwarder doesn't answer</td>
<td>Every query that reaches it waits out its timeout first. Forwarder health is your job: "DNS Server doesn't determine whether an unresponsive forwarder is offline or slow. Monitor forwarder health separately."</td>
<td>Remove or replace it (Step 10)</td>
</tr>
<tr>
<td>Four or more forwarders</td>
<td>"There will not be enough time to arrive to use the fourth forwarder."</td>
<td>Keep two or three healthy ones (Step 10)</td>
</tr>
<tr>
<td>All answer, but some take over a second</td>
<td>"If a forwarder takes more than one second to respond, the server considers the response slow for reordering purposes."</td>
<td>Replace the slow one, or look at the path to it</td>
</tr>
<tr>
<td>Windows Server 2022 or later, every forwarder failed once (a link outage) and lookups still fail after the link is back</td>
<td>"In Windows Server 2022 and later, if none of the forwarders respond, DNS Server uses only the first server in the dynamic list for subsequent queries until the DNS Server service restarts."</td>
<td>Step 10 (reordering), not a blind restart</td>
</tr>
<tr>
<td>A forwarder answers your direct test with Refused, or with "Nonexistent domain" for a real name</td>
<td>A terminal answer, so no fallback: "A DNS response such as NXDOMAIN or NODATA is a valid, terminal response and doesn't normally trigger fallback." An ISP resolver that only serves its own customers refuses queries from a new public IP after a firewall or ISP change (our reading)</td>
<td>Replace that forwarder (Step 10)</td>
</tr>
<tr>
<td>The forwarder is another internal DNS server</td>
<td>Microsoft: "If the server is healthy and can forward queries, repeat this step, and examine the server to which this server forwards queries."</td>
<td>Repeat Steps 3-5 there</td>
</tr>
<tr>
<td>Every forwarder answers fast from the DNS server</td>
<td>Forwarders aren't the problem</td>
<td>Steps 7-9</td>
</tr>
</tbody></table>
<p><code>Test-NetConnection -Port 53</code> proves nothing here: its <code>-Port</code> parameter "Specifies the TCP port number on the remote computer.", and most DNS queries go over UDP (Step 9).</p>
<hr />
<h2>Step 6: Can root hints fallback actually happen? (read-only)</h2>
<p>Root hints are the server's way to resolve without forwarders: "Root hints are resource records that identify DNS servers authoritative for the root of a DNS namespace." With forwarders configured, they're only a fallback, and Microsoft lists four conditions for it, including "The UseRootHint setting is $true" and "The server has usable root hint data and network access to the required DNS servers."</p>
<pre><code class="language-powershell"># Read-only. On the DNS server: are root hints present, and is fallback allowed?
Get-DnsServerRootHint
(Get-DnsServerForwarder).UseRootHint
# Microsoft's built-in root hints check (the address is the DNS server's own)
Test-DnsServer -IPAddress 10.0.0.10 -Context RootHints
# Can this server reach a root server directly? (198.41.0.4 is a.root-servers.net; take any address from Get-DnsServerRootHint)
Resolve-DnsName -Name '.' -Type NS -Server 198.41.0.4 -DnsOnly -NoRecursion
</code></pre>
<p>Microsoft's own root test uses interactive nslookup against the server being examined:</p>
<pre><code class="language-console">:: Read-only. On the DNS server (Microsoft's form; the address is the DNS server's own)
nslookup
server 10.0.0.10
set q=NS
</code></pre>
<p>Per that article, "If the resolver returns the IP address of a root server, you probably have a broken delegation" further down, and a timeout means checking the root hints, the network, or a firewall: "It's also possible that the recursive time-out default is too short."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>UseRootHint</code> is False and the forwarders are down</td>
<td>Failure by design: "the DNS server forwards unresolved queries only to the DNS servers in the forwarders list and does not try iterative queries if the forwarders do not resolve the queries."</td>
<td>Fix the forwarders (Step 5), or decide on purpose (Step 10)</td>
</tr>
<tr>
<td>No root hints listed</td>
<td>Nothing to fall back to, and nothing to start from without forwarders</td>
<td>Step 10 (import root hints)</td>
</tr>
<tr>
<td>The direct root query times out from the DNS server, forwarders answer</td>
<td>The firewall allows DNS out only to the forwarders. Fallback can't work and only adds delay (our reading)</td>
<td>Decide: allow it, or set <code>UseRootHint</code> to False on purpose</td>
</tr>
<tr>
<td>Root servers answer, forwarders are dead, and the client still gets "Server failure"</td>
<td>The timer ran out first: "If the overall recursion timeout expires first, the query fails without reaching the root hints."</td>
<td>Fewer, healthy forwarders (Step 10)</td>
</tr>
<tr>
<td>Root hints work, no forwarders configured</td>
<td>Normal iterative resolution</td>
<td>Step 9 if specific domains still fail</td>
</tr>
</tbody></table>
<p>Microsoft's design note is worth reading before you change anything: keep fallback "when direct iterative resolution is acceptable" and "Disable it when policy requires all unresolved queries to remain within the forwarding boundary, and enforce the boundary with network controls."</p>
<hr />
<h2>Step 7: Does the conditional forwarder exist on this DC, and do its targets answer? (read-only)</h2>
<p>A conditional forwarder is stored as a zone: "DNS Server stores conditional forwarders as forwarder zones." That means it has a storage location and a replication scope, and a DC that doesn't have the zone sends the partner name down the general forwarder path instead. Read it on every DC:</p>
<pre><code class="language-powershell"># Read-only. Every DC: is the fabrikam.com conditional forwarder there, and how is it set? (our form)
foreach ($dc in (Get-ADDomainController -Filter *).HostName) {
  Get-DnsServerZone -ComputerName $dc -Name fabrikam.com -ErrorAction SilentlyContinue |
    Select-Object @{ n = 'DC'; e = { $dc } }, ZoneName, ZoneType, IsDsIntegrated, ReplicationScope, MasterServers, ForwarderTimeout, UseRecursion
}
</code></pre>
<pre><code class="language-console">:: Read-only. One DC at a time: list only conditional forwarder zones (dnscmd's /forwarder filter)
dnscmd DC2 /enumzones /forwarder
</code></pre>
<p>Then ask each target server directly, from the DC that fails:</p>
<pre><code class="language-powershell"># Read-only. From the failing DC: does each conditional forwarder target answer? (addresses are examples)
Resolve-DnsName -Name portal.fabrikam.com -Server 192.0.2.10 -DnsOnly -QuickTimeout
Resolve-DnsName -Name portal.fabrikam.com -Server 192.0.2.11 -DnsOnly -QuickTimeout
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The zone exists on head-office DCs, not on the Dallas DC</td>
<td>Not AD-integrated (for that kind, "The entries are stored in the registry." of one server), or AD-integrated with a scope that doesn't include Dallas. The scopes are Custom, Domain, Forest, and Legacy</td>
<td>Step 10 (replication scope)</td>
</tr>
<tr>
<td>Present everywhere, the targets time out from Dallas only</td>
<td>The VPN or firewall path from that site to the partner's DNS servers (our reading)</td>
<td>Fix the path; Step 9 for UDP and TCP</td>
</tr>
<tr>
<td>Three or more targets</td>
<td>The conditional-forwarder timer defaults to 5 seconds per target, so "a Windows DNS server will be able to query at most two conditional forwarders."</td>
<td>Order the healthy ones first (Step 10)</td>
</tr>
<tr>
<td>A target answers Refused</td>
<td>Microsoft's scenario: a policy on the target refuses the query, and "The DNS server won't contact the second conditional forwarder as we already got a response from the first one"</td>
<td>Ask the partner, or check <code>Get-DnsServerQueryResolutionPolicy</code> if it's a Windows server you run</td>
</tr>
<tr>
<td>A target answers "Nonexistent domain" for some names only</td>
<td>Missing records on the target: "Register the missing records on the forwarder for the zone in question, either statically or dynamically."</td>
<td>The partner's zone, not your forwarder</td>
</tr>
<tr>
<td><code>UseRecursion</code> False and the targets are down</td>
<td>Failure by design; nothing else is tried</td>
<td>Fix the targets</td>
</tr>
<tr>
<td>You try to create it and get a zone-exists error</td>
<td>"You can't create a conditional forwarder for a DNS name that matches a primary, secondary, or stub zone that the same DNS server hosts."</td>
<td>Step 4</td>
</tr>
</tbody></table>
<p>Azure private endpoints are a common version of this. Microsoft: "DNS queries for private endpoints must originate from the virtual network that is linked to the private DNS zone." So an on-premises conditional forwarder that points straight at Azure's 168.63.129.16 can't work; it points at a DNS forwarder or Private Resolver inside the linked virtual network, and "The conditional forwarding must be made to the recommended public DNS zone forwarder." (for example the public service zone rather than the privatelink zone).</p>
<hr />
<h2>Step 8: Is a cached answer hiding the fix? (read-only)</h2>
<p>Both the DNS server and the client cache negative answers. Read before you clear:</p>
<pre><code class="language-powershell"># Read-only. On the DNS server: what's cached for the failing name, and the cache limits
Show-DnsServerCache | Where-Object { $_.HostName -like '*fabrikam*' }
Get-DnsServerCache
</code></pre>
<pre><code class="language-powershell"># Read-only. On the client: what the DNS Client service has cached for the name
Get-DnsClientCache -Entry portal.fabrikam.com -ErrorAction SilentlyContinue
</code></pre>
<p><code>Get-DnsServerCache</code> returns the limits, including "MaxTTL, MaxNegativeTTL"; Microsoft's example shows a <code>MaxNegativeTTL</code> of 15 minutes, so a negative answer cached before the fix can linger that long (our reading). On the client, Microsoft's check is <code>ipconfig /displaydns</code>: a "Name does not exist" entry means a negative response "was returned from a DNS server and was cached on the client."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>A negative entry for the name on the server, upstream already fixed</td>
<td>It ages out on its own; clearing it once is fine after the fix (Step 10)</td>
<td>Clear once, then re-test</td>
</tr>
<tr>
<td>Works after <code>Clear-DnsServerCache</code>, then fails again later through a conditional forwarder</td>
<td>Microsoft's dual-stack case: a bad AAAA answer from a third-party target "poisons the local DNS server cache and generates a negative cache entry for the host A record, too."</td>
<td>Ask the target's vendor; Microsoft's workaround is a recursion policy that denies AAAA queries for that path</td>
</tr>
<tr>
<td>A stale positive answer (old IP) on the server</td>
<td>The record's TTL hasn't run out</td>
<td>Wait it out, or clear once</td>
</tr>
<tr>
<td>Negative entry on the client only</td>
<td>The client cached a failure from earlier</td>
<td><code>ipconfig /flushdns</code> on that client after the server is fixed</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Is the network dropping large or TCP DNS? (read-only, then one optional logging change)</h2>
<p>DNS uses both transports. Microsoft's port page: "Messages are sent over UDP and DNS servers bind to UDP port 53." When an answer is too big, "The DNS client (message sender) can then choose to reissue the request to the DNS server using TCP (over TCP port 53)." And Windows DNS advertises bigger UDP answers by default through EDNS0, which some firewalls drop: "EDNS0 allows larger User Datagram Protocol (UDP) packet sizes. However, some firewall programs may not allow UDP packets that are larger than 512 bytes."</p>
<pre><code class="language-powershell"># Read-only. From the DNS server: the same name over UDP and over TCP to one forwarder (address is an example)
Resolve-DnsName -Name portal.fabrikam.com -Server 203.0.113.53 -DnsOnly
Resolve-DnsName -Name portal.fabrikam.com -Server 203.0.113.53 -DnsOnly -TcpOnly
# The server's EDNS settings
Get-DnsServerEDns
</code></pre>
<p><code>-TcpOnly</code> is documented as "Uses only TCP for this query." Microsoft's symptom for the EDNS case: "After you deploy a Windows-based DNS server, DNS queries to some domains may not be resolved successfully."</p>
<p>To see what the server itself sent and what timed out, the DNS Server analytic log records outbound recursive queries and timeouts (event 260 is RECURSE_QUERY_OUT, 262 is RECURSE_QUERY_TIMEOUT, 258 is RESPONSE_FAILURE). It's off by default; Microsoft's steps turn it on in Event Viewer (<strong>Applications and Services Logs</strong> → <strong>Microsoft</strong> → <strong>Windows</strong> → <strong>DNS-Server</strong> → <strong>View</strong> → <strong>Show Analytic and Debug Logs</strong> → <strong>Analytical</strong> → <strong>Properties</strong> → <strong>Enable logging</strong>). Microsoft's cost note: "There's no apparent performance impact for query rates of 50,000 QPS and lower." Reproduce once, then read it:</p>
<pre><code class="language-powershell"># Read-only. On the DNS server: recursive timeouts and response failures from the analytic log (our form; -Oldest is required for analytic logs)
Get-WinEvent -LogName 'Microsoft-Windows-DNSServer/Analytical' -Oldest -ErrorAction SilentlyContinue |
  Where-Object { $_.Id -in 258, 262 } |
  Select-Object -Property TimeCreated, Id, Message -Last 40 | Format-List
</code></pre>
<p><code>-Oldest</code> "is required to get events from .etl and .evt files and from debug and analytic logs." If the read errors while the log is enabled, disable the log first and read it then (our reading of Microsoft's note that a circular log can't be viewed while it's being written).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>UDP fails, TCP works, for the same name</td>
<td>A firewall drops large or EDNS UDP answers. Resolution: "update the firewall program to recognize and allow UDP packets that are larger than 512 bytes."</td>
<td>The firewall first; Step 10 has Microsoft's workaround</td>
</tr>
<tr>
<td>UDP works, TCP fails</td>
<td>TCP 53 blocked; any truncated answer then fails (our reading)</td>
<td>Allow TCP 53 between the DNS server and its forwarders</td>
</tr>
<tr>
<td>Event 262 with the same <code>Destination</code> again and again</td>
<td>That forwarder or target isn't answering</td>
<td>Step 5 or 7 for that address</td>
</tr>
<tr>
<td>Event 258 for the name</td>
<td>Read the <code>Reason</code> and <code>RCODE</code> fields in the message</td>
<td>Map it back to Steps 3-7</td>
</tr>
<tr>
<td>Both transports fail to every forwarder</td>
<td>Outbound DNS from the DNS server is blocked, often after a firewall change (our reading)</td>
<td>Allow UDP and TCP 53 from the DNS servers to the forwarders</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>What people try</th>
<th>Why it misleads (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Pointing PCs or DCs at a public resolver</td>
<td>Internal names and AD records stop resolving. Microsoft: "Don't configure the DNS client settings on the domain controllers to point to DNS servers of your Internet Service Provider (ISP)." Forward from your DNS servers instead; <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> shows what that does between DCs</td>
</tr>
<tr>
<td>Adding more forwarders</td>
<td>Past three, the extras are never reached with default timers (Step 5), and each dead one adds seconds</td>
</tr>
<tr>
<td>Restarting the DNS Server service on every DC</td>
<td>It clears the cache and, on Windows Server 2022 and later, the stuck forwarder state, so the symptom goes away until the next outage. You lose the evidence of which forwarder failed</td>
</tr>
<tr>
<td>Clearing the server cache over and over</td>
<td>It hides the pattern that points at negative caching (Step 8)</td>
</tr>
<tr>
<td><code>Test-NetConnection</code> to port 53</td>
<td>It tests TCP only; DNS answers mostly come back over UDP</td>
</tr>
<tr>
<td>Pinging the forwarder</td>
<td>ICMP can pass while DNS is blocked, and the reverse</td>
</tr>
<tr>
<td>Turning on Disable recursion to stop the delays</td>
<td>Microsoft notes "This setting also disables forwarders." External names then fail outright with Refused</td>
</tr>
<tr>
<td>Raising every timeout to the maximum</td>
<td>A dead forwarder now costs more time per query, and clients give up first. Microsoft's own rule: "DNS: The recursion timeout must be greater than the forwarding timeout"</td>
</tr>
<tr>
<td>Disabling EDNS before testing UDP against TCP</td>
<td>It may work around a firewall bug without telling you which firewall (Step 9)</td>
</tr>
<tr>
<td>Recreating a conditional forwarder on one DC</td>
<td>Without the right replication scope, the next DC still won't have it (Step 7)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one link, then prove it</h2>
<p>Only after Steps 1-9 agree on which link failed. Write down the current state first: <code>Get-DnsServerForwarder</code>, <code>Get-DnsServerRecursion</code>, <code>Get-DnsServerRootHint</code>, the Step 7 conditional forwarder table, and <code>Get-DnsServerQueryResolutionPolicy</code> from every DNS server (our habit). <code>Set-DnsServerForwarder</code> replaces the whole list: "This cmdlet overwrites existing server level forwarders."</p>
<p><strong>The client asks the wrong server (Step 2).</strong> Fix the DHCP scope's DNS server option, or the static setting. Microsoft's command for a static client (interface name and addresses are examples):</p>
<pre><code class="language-powershell"># Change. On ONE client: point the adapter at the internal DNS servers (Microsoft's form, two addresses)
Set-DnsClientServerAddress -InterfaceAlias 'Ethernet' -ServerAddresses ('10.20.0.10', '10.0.0.10')
</code></pre>
<p><strong>Recursion off, or a policy refusing (Step 3).</strong> Turn recursion on only on servers that serve internal clients; Microsoft warns "Allow recursive queries only from trusted clients and networks, and don't expose recursive DNS service to untrusted networks." If selective recursion is by design, fix the policy's subnet or interface criteria instead.</p>
<pre><code class="language-powershell"># Change. On an internal-only DNS server: allow recursion
Set-DnsServerRecursion -Enable $true -PassThru
</code></pre>
<p><strong>A root zone or an internal zone answering (Step 4).</strong> Delete the <code>.</code> zone in DNS Manager under <strong>Forward Lookup Zones</strong>, per Microsoft's DC guidance. For an external name under your internal zone name, add the record to the internal zone; don't create a conditional forwarder for a name you host.</p>
<p><strong>A dead, slow, or refusing forwarder (Step 5).</strong> Replace the list with two or three forwarders you've tested from the DNS server, preferred one first:</p>
<pre><code class="language-powershell"># Change. On the DNS server: replace the whole forwarder list (it overwrites; addresses are examples)
Set-DnsServerForwarder -IPAddress 203.0.113.53, 198.51.100.53 -PassThru
# Or remove just the dead one
Remove-DnsServerForwarder -IPAddress 192.0.2.53 -PassThru
</code></pre>
<p>On Windows Server 2022 or later, if you want the older behavior of cycling through every forwarder after they've all failed, Microsoft's switch is "To make the server cycle through all configured forwarders as in earlier versions, disable dynamic reordering with Set-DnsServerForwarder -EnableReordering $false." Change timers last, and keep the recursion timeout above the forwarder timeout (<code>Set-DnsServerRecursion -Timeout</code>, <code>Set-DnsServerForwarder -Timeout</code>; both cap at 15 seconds per their references).</p>
<p><strong>Root hints (Step 6).</strong> If they're missing, Microsoft's cmdlet copies them from another DNS server: "The root hints that you copy will not overwrite any existing root hints." If policy keeps all lookups inside the forwarders, make that explicit rather than leaving a fallback that can't work:</p>
<pre><code class="language-powershell"># Change. On the DNS server: copy root hints from a healthy DNS server (example name), or turn fallback off on purpose
Import-DnsServerRootHint -NameServer dc1.contoso.com -PassThru
Set-DnsServerForwarder -UseRootHint $false -PassThru
</code></pre>
<p><strong>The conditional forwarder (Step 7).</strong> Store it in AD with a scope that covers every DC that needs it, and list only targets you've tested:</p>
<pre><code class="language-powershell"># Change. On one DC: create the forwarder in AD, replicated to every DNS server on a DC in the forest (addresses are examples)
Add-DnsServerConditionalForwarderZone -Name fabrikam.com -ReplicationScope Forest -MasterServers 192.0.2.10, 192.0.2.11 -PassThru
# Or fix an existing one: targets and per-zone timeout
Set-DnsServerConditionalForwarderZone -Name fabrikam.com -MasterServers 192.0.2.10, 192.0.2.11 -ForwarderTimeout 5 -PassThru
</code></pre>
<p>If a registry-stored copy already exists on one DC, remove that copy first (export its settings) so the AD-integrated one doesn't collide with it (our reading). Microsoft's forest scope means "Replicate the conditional forwarder to all DNS servers that run on domain controllers in the forest."</p>
<p><strong>Cache (Step 8).</strong> Once, after the fix, on the server and then the client:</p>
<pre><code class="language-powershell"># Change (low risk). On the DNS server, then on the client that cached the failure
Clear-DnsServerCache -Force
Clear-DnsClientCache
</code></pre>
<p><code>Clear-DnsClientCache</code> is documented as "equivalent to running ipconfig /flushdns."</p>
<p><strong>Large UDP or TCP 53 dropped (Step 9).</strong> Fix the firewall. Microsoft's workaround, if you can't yet, turns off EDNS probes on the DNS server (a registry change; reverse it with <code>1</code> after the firewall is fixed):</p>
<pre><code class="language-console">:: Change. On the DNS server: Microsoft's EDNS0 workaround (zero, not the letter O)
dnscmd /config /enableednsprobes 0
</code></pre>
<p>Turn the analytic log back off when you're done (our habit).</p>
<p><strong>Then prove it.</strong> From a failing client and from each DNS server: <code>Resolve-DnsName -Name www.microsoft.com -Server &lt;each DNS server&gt; -DnsOnly</code> answers in well under a second, the partner name resolves from the Dallas DC as well as head office, <code>Test-DnsServer -Context Forwarder</code> returns Success, and the analytic log shows no new event 262 for your forwarders (our reading). Then let the helpdesk retest the original sites.</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Client asks the wrong server</td>
<td>Fix the DHCP option or static setting to internal DNS servers</td>
<td>Public resolvers on domain-joined PCs or DCs</td>
</tr>
<tr>
<td>Recursion refused</td>
<td>Recursion on for internal clients, or a corrected recursion policy</td>
<td>An open resolver reachable from the internet</td>
</tr>
<tr>
<td>Dead or refusing forwarder</td>
<td>Two or three tested forwarders, preferred one first</td>
<td>Five more forwarders on top of the dead one</td>
</tr>
<tr>
<td>Forwarders fine, fallback broken</td>
<td>Root hints reachable, or <code>UseRootHint</code> off on purpose</td>
<td>Raising every timeout to 15 seconds</td>
</tr>
<tr>
<td>Conditional forwarder missing on one site's DC</td>
<td>AD-integrated forwarder with Forest or Domain scope</td>
<td>Registry copies created by hand on each DC</td>
</tr>
<tr>
<td>Stale negative answer</td>
<td>One cache clear after the fix</td>
<td>Clearing the cache as the fix</td>
</tr>
<tr>
<td>Firewall drops large UDP answers</td>
<td>Firewall allows EDNS and TCP 53</td>
<td>EDNS off with no ticket to turn it back on</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why is my Windows DNS server not resolving external names?</strong>
Most often a forwarder is dead or refusing and the recursion timer runs out before root hints are tried, recursion is disabled or limited by a policy, or an internal zone answers for the name. Microsoft's first split: "Check whether the resolution fails from the DNS server itself." Then read <code>Get-DnsServerForwarder</code> and test each forwarder directly.</p>
<p><strong>Should I use DNS forwarders or root hints?</strong>
Either works; forwarders centralize lookups, root hints resolve without them. Microsoft keeps root hints as a fallback by default and says to turn it off "when policy requires all unresolved queries to remain within the forwarding boundary". Don't leave fallback on if the firewall blocks it.</p>
<p><strong>Why is my DNS conditional forwarder not working?</strong>
Usually it doesn't exist on the DC the client asked (registry-stored or the wrong replication scope), its targets don't answer from that site, or a target refuses or lacks the record. Run Step 7 on every DC, then query each target directly.</p>
<p><strong>How many DNS forwarders should I configure?</strong>
Two or three healthy ones. With default timers, Microsoft says the server can reach "at most three forwarders", and every dead one in front adds seconds to each lookup.</p>
<p><strong>Why does nslookup work but ping doesn't?</strong>
nslookup asks the DNS server directly; ping goes through the DNS Client service's cache, which also holds hosts file entries and earlier negative answers. Microsoft: "Because nslookup doesn't use the client's DNS cache, name resolution will use the client's configured DNS server." Check the client cache and hosts file (Steps 2 and 8).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (when DCs can't find a domain controller through DNS at boot)</li>
<li><a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> (when internal records go missing instead)</li>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (DC DNS client settings and the ports between DCs)</li>
<li><a href="https://dev.to/adminpackstudio/windows-server-dhcp-not-assigning-ip-addresses-a-read-first-scope-authorization-relay-and-d13">Windows Server DHCP Not Assigning IP Addresses</a> (when the scope hands clients the wrong DNS servers)</li>
</ul>
<h2>Free checklist</h2>
<p>Adding five forwarders and restarting DNS on every DC before anyone tested a single forwarder is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-dns-server-not-resolving-external-names">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-dns-server-not-resolving-external-names</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-dns-server-not-resolving-external-names">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-dns-server-not-resolving-external-names</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>Get-DnsClientServerAddress</code>, <code>Get-DnsClientCache</code>, <code>Resolve-DnsName</code>, <code>Get-Service</code>, <code>Get-DnsServerRecursion</code>, <code>Get-DnsServerRecursionScope</code>, <code>Get-DnsServerQueryResolutionPolicy</code>, <code>Get-DnsServerSetting</code>, <code>Get-DnsServerZone</code>, <code>Get-DnsServerForwarder</code>, <code>Get-DnsServerRootHint</code>, <code>Test-DnsServer</code>, <code>Show-DnsServerCache</code>, <code>Get-DnsServerCache</code>, <code>Get-DnsServerEDns</code>, <code>Get-WinEvent</code>, <code>dnscmd /enumzones</code>, and <code>nslookup</code>. Enabling the analytic log, changing client DNS settings, recursion, forwarders, timeouts, reordering, root hints, conditional forwarders, or EDNS, and clearing caches are deliberate changes. Change one link at a time, test from the failing client and from each DNS server, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[LDAPS Not Working on a Domain Controller: A Read-First Certificate, LDAP Signing, and Event 2889 Triage]]></title><description><![CDATA[LDAPS not working on a DC? Read the certificate it serves on 636, client trust, Event 2889 binds, and the signing policy before changing them.]]></description><link>https://adminpackstudio.hashnode.dev/ldaps-not-working-on-a-domain-controller-a-read-first-certificate-ldap-signing-and-event-2889-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/ldaps-not-working-on-a-domain-controller-a-read-first-certificate-ldap-signing-and-event-2889-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[Security]]></category><category><![CDATA[sysadmin]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 02:42:16 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since we added DC3 on Windows Server 2025 last week, the door-badge server's directory sync fails with 'Strong Authentication Required' whenever it lands on DC3. The firewall's LDAPS test against DC2 says 'Cannot open connection' since DC2's certificate renewed, and every DC logs Event 2887 once a day. A forum post says to turn LDAP signing off in the Default Domain Controllers Policy, delete the old certificates on DC2, and set channel binding to Never. Can we start with that?"</p>
<p>Not yet. <strong>"LDAPS not working" and "LDAP signing required" are two different breaks that show up in the same week. LDAPS on port 636 fails when the domain controller has no certificate it can use, serves a different certificate than you think, or serves one the client doesn't trust or can't match to the name it used. Signing and channel binding failures are binds the DC now rejects on purpose.</strong> Microsoft: "There's no user interface for configuring LDAPS. Installing a valid certificate on a domain controller permits the LDAP service to listen for, and automatically accept, SSL connections for both LDAP and global catalog traffic." On signing, Microsoft's own warning: "After you make this configuration change, clients that rely on unsigned SASL (Negotiate, Kerberos, NTLM, or Digest) LDAP binds or on LDAP simple binds over a non-SSL/TLS connection stop working." Turning signing off domain-wide, deleting certificates, or setting channel binding to Never each removes evidence you need, and the first and last reopen the attacks those settings exist to stop. Work through it as a chain: <strong>which client, which DC, which port, which bind</strong> → <strong>does the DC listen on 636 and does the client reach it?</strong> → <strong>does the DC have a certificate it can use (Event 1220)?</strong> → <strong>which certificate does it actually serve?</strong> → <strong>does the client trust that certificate?</strong> → <strong>does the name the client used match it?</strong> → <strong>who is binding unsigned (Events 2887 and 2889)?</strong> → <strong>which signing policy is actually in effect on each DC?</strong> → <strong>is channel binding rejecting a TLS bind?</strong> <code>Test-NetConnection</code>, <code>Get-ChildItem Cert:\LocalMachine\My</code>, <code>certutil</code>, <code>Get-WinEvent</code>, <code>reg query</code>, <code>gpresult</code>, and <code>ldp.exe</code> are the sources for that chain. Turn signing off everywhere and you'll find out later which client was sending passwords in clear text.</p>
<p>If the DC never got a certificate because enrollment fails, <a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> covers the GPO, template, and CA side. If <code>certutil</code> on the client fails the revocation check, <a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline 0x80092013</a> covers CRLs and CDPs. This post covers <strong>"LDAPS on port 636 (or 3269) to a domain controller fails, an application or appliance gets 'Strong Authentication Required' after LDAP signing changes or a new Windows Server 2025 DC, or Directory Service Events 1220, 2886, 2887, 2889, 3039, or 3075 appear, and you need a read-first path before you replace certificates, restart DCs, change signing or channel binding policy, or edit NTDS registry values."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>LDAPS is a certificate problem first.</strong> Microsoft's requirements include: "The Enhanced Key Usage extension includes the Server Authentication (1.3.6.1.5.5.7.3.1) object identifier (also known as OID)." and the DC's FQDN in the subject CN or a SAN DNS entry (Step 3).</li>
<li><strong>The DC may not serve the certificate you're looking at.</strong> Microsoft: "If there is a certificate in the NT Directory Services (NTDS) store, DC use the certificate in the NTDS store instead." (Step 4).</li>
<li><strong>The client has to trust it.</strong> Microsoft: "The certificate was issued by a CA that the domain controller and the LDAPS clients trust." (Step 5).</li>
<li><strong>Event 2889 names the unsigned clients.</strong> Microsoft: "The log entry displays the IP address of the client and the identity that the client tried to use to authenticate." It's off until you raise a diagnostic level (Step 7).</li>
<li><strong>Windows Server 2025 changes the default.</strong> Microsoft: "Windows Server 2025 or later requires LDAP signing by default for new Active Directory deployments." (Step 8).</li>
<li><strong>Channel binding only covers TLS plus SASL.</strong> Microsoft: "LDAP channel binding applies through the Domain controller: LDAP server channel binding token requirements policy only to TLS-secured sessions that use Simple Authentication and Security Layer (SASL) authentication." (Step 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which client, which DC, which port, which bind type, since when</td>
<td>"LDAP is broken" with no DC or port named</td>
</tr>
<tr>
<td>2. Port and listener</td>
<td>The DC listens on 636 (and 3269 on a global catalog) and the client reaches it</td>
<td>TCP test fails; nothing listening</td>
</tr>
<tr>
<td>3. A usable certificate</td>
<td>The DC holds a Server Authentication certificate with its FQDN and a private key</td>
<td>Event 1220 on the DC, Schannel 36886</td>
</tr>
<tr>
<td>4. The served certificate</td>
<td>You know which certificate the DC presents on 636</td>
<td>Renewed certificate in the store, old one still served</td>
</tr>
<tr>
<td>5. Client trust</td>
<td>The client builds a valid chain and can check revocation</td>
<td>Untrusted root, expired, revocation offline</td>
</tr>
<tr>
<td>6. Name match</td>
<td>The name the client uses is in the certificate</td>
<td>Works by FQDN, fails by domain name, alias, or IP</td>
</tr>
<tr>
<td>7. Unsigned binds</td>
<td>You know who binds unsigned or in clear text on 389</td>
<td>Event 2887 every day, no client named</td>
</tr>
<tr>
<td>8. Policy in effect</td>
<td>You know each DC's signing setting, including the 2025 enforcement policy</td>
<td>One DC rejects, the others allow</td>
</tr>
<tr>
<td>9. Channel binding</td>
<td>You know whether TLS + SASL binds would fail CBT</td>
<td>Events 3039, 3074, or 3075</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one link, one change</td>
<td>Signing off everywhere, same failure</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which client or application</strong></td>
<td>A Windows computer, an appliance, a Java or Linux app, a SaaS connector. Microsoft splits devices into appliances, non-Windows devices, and Windows devices when it plans signing and channel binding</td>
</tr>
<tr>
<td><strong>Which DC, and how the client finds it</strong></td>
<td>A single DC FQDN, the domain name (which resolves to any DC), a load-balanced or alias name, or an IP address. The certificate has to carry the name used (Step 6)</td>
</tr>
<tr>
<td><strong>Which port</strong></td>
<td>389 (LDAP), 636 (LDAPS), 3268 (global catalog), 3269 (global catalog over SSL). Microsoft: "LDAPS communication occurs over port TCP 636. LDAPS communication to a global catalog server occurs over TCP 3269."</td>
</tr>
<tr>
<td><strong>Which bind type</strong></td>
<td>Simple bind (a DN or user name and password) or SASL (Negotiate, Kerberos, NTLM). Signing and channel binding treat them differently (Steps 7 and 9)</td>
</tr>
<tr>
<td><strong>Exact error text</strong></td>
<td>"Cannot open connection" in ldp, "Strong Authentication Required", a TLS or certificate error in the app's log, or a timeout</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>A certificate renewal, a new CA, a new DC (especially Windows Server 2025), a GPO change, a TLS hardening change, a firewall change</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Timeout on 636, or the TCP test fails</td>
<td>Firewall, routing, or nothing listening</td>
<td>Step 2</td>
</tr>
<tr>
<td>Event 1220 on the DC, or "Cannot open connection" in ldp on 636</td>
<td>No usable certificate. Microsoft's Event 1220 text: "LDAP over Secure Sockets Layer (SSL) will be unavailable at this time because the server was unable to obtain a certificate."</td>
<td>Step 3</td>
</tr>
<tr>
<td>LDAPS broke right after a renewal</td>
<td>The DC still serves the old certificate, or picked a different one</td>
<td>Step 4</td>
</tr>
<tr>
<td>Windows clients work, an appliance or Java app fails on 636</td>
<td>The app doesn't trust your CA, or uses a name not in the certificate</td>
<td>Steps 5 and 6</td>
</tr>
<tr>
<td>Works by DC FQDN, fails by domain name or alias</td>
<td>Name not in the certificate</td>
<td>Step 6</td>
</tr>
<tr>
<td>"Strong Authentication Required" on 389</td>
<td>The DC requires signing; the client sends a simple bind in clear text or an unsigned SASL bind</td>
<td>Steps 7 and 8</td>
</tr>
<tr>
<td>Only one DC rejects binds</td>
<td>That DC's signing policy differs, often a new Windows Server 2025 DC</td>
<td>Step 8</td>
</tr>
<tr>
<td>Events 3039, 3074, or 3075</td>
<td>TLS + SASL binds without a valid channel binding token</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Does the DC listen on 636, and does the client reach it? (read-only)</h2>
<p>Start at the network, because nothing else matters if the handshake never starts. From the failing client, or a Windows box on the same network segment:</p>
<pre><code class="language-powershell"># Read-only. From the client side: can it open TCP 636 (and 3269 for the global catalog) on the DC it uses?
Test-NetConnection -ComputerName DC2.contoso.com -Port 636
Test-NetConnection -ComputerName DC2.contoso.com -Port 3269
</code></pre>
<p>On the DC itself:</p>
<pre><code class="language-powershell"># Read-only. On the DC: is anything listening on 636 and 3269?
Get-NetTCPConnection -LocalPort 636, 3269 -State Listen -ErrorAction SilentlyContinue |
  Select-Object LocalAddress, LocalPort, State, OwningProcess | Format-Table -AutoSize
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>TcpTestSucceeded : False</code> from the client, listener present on the DC</td>
<td>A firewall or route between them</td>
<td>Fix the path; Microsoft's port reference lists LDAP SSL on TCP 636 and Global Catalog on TCP 3268 and 3269</td>
</tr>
<tr>
<td>Listener present, TCP test succeeds</td>
<td>The port is fine; the problem is in the TLS handshake or the bind</td>
<td>Step 3</td>
</tr>
<tr>
<td>No listener on 636</td>
<td>Unusual for a DC; the LDAP service may not be running</td>
<td>Check the AD DS service and the Directory Service log before going further</td>
</tr>
<tr>
<td>3269 fails but 636 works</td>
<td>The DC isn't a global catalog, or the app should use 636</td>
<td>Point the app at a GC or at 636</td>
</tr>
</tbody></table>
<p>A port that answers isn't the same as LDAPS working. The TCP test only proves the socket opens; a DC with no certificate still accepts the connection and then fails the TLS handshake (our reading of Microsoft's Event 1220 text and the Schannel 36886 description).</p>
<hr />
<h2>Step 3: Does the DC have a certificate it can use? (read-only)</h2>
<p>Microsoft's requirements for an LDAPS certificate, in short: it sits in the Local Computer's Personal store, "A private key that matches the certificate is present in the Local Computer's store and is correctly associated with the certificate," the private key doesn't have strong protection, the EKU includes Server Authentication, the DC's Active Directory FQDN is the subject CN or a SAN DNS entry, and it chains to a CA both the DC and the clients trust.</p>
<p>First, the DC's own log. Microsoft: "Event ID 1220 is logged on a domain controller when client computers attempt to make an LDAP-over-SSL connection to the directory when SSL connections are not enabled on the directory." Schannel logs its side in the System log; Microsoft's Schannel reference describes Event 36886 as occurring "when a server attempt to make an SSL connection but no server certificate is found."</p>
<pre><code class="language-powershell"># Read-only. On the DC: Event 1220 (Directory Service) and Schannel 36886 (System), last 7 days
$since = (Get-Date).AddDays(-7)
Get-WinEvent -FilterHashtable @{ LogName = 'Directory Service'; Id = 1220; StartTime = $since } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'Schannel'; Id = 36886; StartTime = $since } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>Then the certificates in the computer's Personal store that could serve LDAPS:</p>
<pre><code class="language-powershell"># Read-only. On the DC: Server Authentication certificates in the computer's Personal (MY) store
Get-ChildItem -Path Cert:\LocalMachine\My -SSLServerAuthentication |
  Select-Object Subject, Issuer, NotBefore, NotAfter, HasPrivateKey, Thumbprint, DnsNameList, EnhancedKeyUsageList |
  Format-List
</code></pre>
<p>And Microsoft's validity check from the Directory Services team, which also reports the private key:</p>
<pre><code class="language-console">:: Read-only. On the DC: verify each certificate in the computer's Personal store (Microsoft's AskDS form)
certutil -verifystore MY
</code></pre>
<p>Per that post, a good certificate shows "Encryption test passed", the Server Authentication policy under "Verified Application Policies", and "Certificate is valid" at the end. The missing-key case reads "No key provider information" and "Missing stored keyset".</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No Server Authentication certificate at all</td>
<td>Microsoft: "If you want to configure a domain controller or an AD LDS server to support SSL connections, you must provide a certificate for the AD DS or AD LDS directory to use."</td>
<td>Step 10, and the autoenrollment post if enrollment fails</td>
</tr>
<tr>
<td><code>HasPrivateKey</code> is False, or "Missing stored keyset"</td>
<td>AskDS: "The normal cause of this problem is that the certificate request was generated on one machine and we have installed the certificate on a different machine."</td>
<td>Step 10</td>
</tr>
<tr>
<td><code>NotAfter</code> is in the past</td>
<td>Expired. AskDS: "You cannot renew a certificate once it has expired."</td>
<td>Step 10 (new certificate)</td>
</tr>
<tr>
<td>The FQDN isn't in <code>Subject</code> or <code>DnsNameList</code></td>
<td>Fails the name requirement</td>
<td>Step 6</td>
</tr>
<tr>
<td>Several valid certificates</td>
<td>Microsoft: "If there are multiple valid certificates available in the local computer store, Schannel may not select the correct certificate."</td>
<td>Step 4</td>
</tr>
<tr>
<td>One good certificate, Event 1220 still logged after it was added</td>
<td>The DC may not have reloaded it yet (our reading)</td>
<td>Step 4</td>
</tr>
</tbody></table>
<p>If you see no 1220 and no 36886 but LDAPS still fails, the DC probably has a certificate; the question moves to which one it serves and whether the client accepts it (our reading).</p>
<hr />
<h2>Step 4: Which certificate does the DC actually serve? (read-only)</h2>
<p>This is where renewals go wrong. Three Microsoft statements together explain it. The NTDS store wins: "AD DS preferentially looks for certificates in this store over the Local Machine's store." In the computer store, the pick isn't predictable: "Schannel, the Microsoft SSL provider, selects the first valid certificate that it finds in the local computer store." And on newer versions: "Finally, if a Windows Server 2008 or a later version domain controller finds multiple certificates in its store, it will random chose one of these certificates." Microsoft's LDAPS troubleshooting article adds: "A conflict with a certification authority (CA) certificate may occur if the CA is installed on a domain controller that you are trying to access through LDAPS."</p>
<p>So read what the DC presents on the wire, not what's in the store. This asks the DC for its certificate the same way an LDAPS client would and prints it, without trusting it:</p>
<pre><code class="language-powershell"># Read-only. From the client side: which certificate does DC2 present on 636? (our form, using .NET SslStream)
# The validation callback returns $true so you can see an untrusted or wrong certificate instead of an exception
$dc  = 'DC2.contoso.com'
$tcp = New-Object System.Net.Sockets.TcpClient($dc, 636)
$ssl = New-Object System.Net.Security.SslStream($tcp.GetStream(), $false, { $true })
try {
  $ssl.AuthenticateAsClient($dc)
  $served = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($ssl.RemoteCertificate)
  $served | Select-Object Subject, Issuer, NotBefore, NotAfter, Thumbprint, DnsNameList | Format-List
  # Save it for Step 5 (public certificate only)
  [System.IO.File]::WriteAllBytes("$env:TEMP\dc2-ldaps.cer", $served.Export('Cert'))
} finally { $ssl.Dispose(); $tcp.Dispose() }
</code></pre>
<p>Change the port to 3269 to read the global catalog side. If <code>AuthenticateAsClient</code> throws before any certificate comes back, the DC had nothing to offer, which matches Event 1220 (our reading).</p>
<p>Compare the served <code>Thumbprint</code> with Step 3's list. Then look in the NTDS store, which the computer-store listing doesn't show. Microsoft's route is the Certificates snap-in: <strong>Service account</strong> → <strong>Local computer</strong> → <strong>Active Directory Domain Services</strong>, then <strong>NTDS\Personal</strong>. A command-line read (our form; the <code>-service</code> option is documented as accessing "a machine service store"):</p>
<pre><code class="language-console">:: Read-only. On the DC: certificates in the AD DS service's own Personal store (our form; use the MMC route if it errors)
certutil -service -store NTDS\My
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Served thumbprint = an old or expired certificate, new one in the computer store</td>
<td>The DC hasn't reloaded. Microsoft's older guidance: "the server must be restarted for Schannel to use the new certificate." Newer DCs can reload without a restart</td>
<td>Step 10 (renewServerCertificate)</td>
</tr>
<tr>
<td>Served certificate is in the NTDS store, not the one you renewed</td>
<td>The NTDS store wins</td>
<td>Replace it there (Step 10)</td>
</tr>
<tr>
<td>Served certificate is the CA's own certificate, or another Server Authentication certificate</td>
<td>Several candidates; the DC chose one you didn't intend</td>
<td>Put the intended certificate in the NTDS store (Step 10)</td>
</tr>
<tr>
<td>Served certificate is the one you expected</td>
<td>The DC side is right</td>
<td>Step 5</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Does the client trust that certificate? (read-only)</h2>
<p>Microsoft's LDAPS troubleshooting article checks the chain from the client, with the certificate exported from the server: <code>certutil -v -urlfetch -verify serverssl.cer &gt; output.txt</code>, then "Open the Output.txt file, and then search for errors." The file saved in Step 4 is the certificate the DC actually served, so use that one:</p>
<pre><code class="language-console">:: Read-only. On the CLIENT (or a Windows box with the same trust store): build the chain and fetch AIA and CDP URLs
certutil -v -urlfetch -verify %TEMP%\dc2-ldaps.cer &gt; %TEMP%\dc2-ldaps-verify.txt
findstr /i "error expired revoked untrusted incomplete offline" %TEMP%\dc2-ldaps-verify.txt
</code></pre>
<p><code>-urlfetch</code> is documented as "Retrieve and verify AIA Certs and CDP CRLs." Microsoft's AskDS post lists what the bottom of the output usually says when it fails:</p>
<table>
<thead>
<tr>
<th><code>certutil</code> says</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>"Expired certificate"</td>
<td>Past its validity period. AskDS: "Now it's possible that the system clock has been changed to an invalid date."</td>
<td>Check the clock first (<a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a>), then Step 10</td>
</tr>
<tr>
<td>"Verifies against UNTRUSTED root"</td>
<td>Per AskDS, a certificate in the chain is missing from the client's Intermediate Certification Authorities store (for an intermediate) or Trusted Root Certification Authorities store (for a root)</td>
<td>Step 10 (trust on the client)</td>
</tr>
<tr>
<td>"Incomplete certificate chain"</td>
<td>An intermediate certificate can't be found or fetched</td>
<td>Step 10</td>
</tr>
<tr>
<td>"Certificate is REVOKED"</td>
<td>AskDS: "the certificate has been revoked and therefore a new certificate needs to be issued."</td>
<td>Step 10 (new certificate)</td>
</tr>
<tr>
<td>"Revocation check skipped" or the revocation server is offline</td>
<td>The client can't reach a CDP</td>
<td><a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline</a></td>
</tr>
<tr>
<td>Verifies clean</td>
<td>The chain is fine on this client</td>
<td>Step 6</td>
</tr>
</tbody></table>
<p>Non-Windows clients (appliances, Java, Linux) don't use the Windows trust store. A clean <code>certutil</code> run on a Windows box proves the chain is buildable; the appliance still needs your root and intermediate imported into its own store (our reading). Microsoft's own requirement covers both sides: "Trust is established by configuring the clients and the server to trust the root CA to which the issuing CA chains."</p>
<p>The same server-certificate pattern (right EKU, private key present, chain the client trusts) is what breaks PEAP on NPS; <a href="https://dev.to/adminpackstudio/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage-26i6">NPS Event 6273 Reason Code 16 or 22</a> walks that version.</p>
<hr />
<h2>Step 6: Does the name the client used match the certificate? (read-only)</h2>
<p>Microsoft's name rule: "The Active Directory fully qualified domain name of the domain controller (for example, dc01.contoso.com) must appear in one of the following places:" the subject CN or a DNS entry in the SAN. A client that connects by the domain name (<code>contoso.com</code>), a load-balancer or alias name (<code>ldap.contoso.com</code>), or an IP address checks that name instead, and a certificate issued only to <code>DC2.contoso.com</code> fails that check (our reading).</p>
<pre><code class="language-powershell"># Read-only. On a Windows client: does the served certificate pass SSL policy for the name the app uses?
# (Microsoft's Test-Certificate example uses -Policy SSL -DNSName 'dns=contoso.com'; the file is the one saved in Step 4)
$served = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("$env:TEMP\dc2-ldaps.cer")
$served.DnsNameList
Test-Certificate -Cert $served -Policy SSL -DNSName 'dns=DC2.contoso.com'
Test-Certificate -Cert $served -Policy SSL -DNSName 'dns=ldap.contoso.com'
</code></pre>
<p>Microsoft's Test-Certificate reference: "If the DNSName parameter is used, then the DNS subject alternative name is used to verify SSL policy." Then read the application's LDAP server setting and the name it uses, and check that name in DNS:</p>
<pre><code class="language-powershell"># Read-only. Where does the name the app uses resolve? (names are examples)
Resolve-DnsName -Name ldap.contoso.com -DnsOnly
Resolve-DnsName -Name contoso.com -Type A -DnsOnly
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The app uses <code>contoso.com</code></td>
<td>It reaches whichever DC DNS returns, and that DC's certificate needs <code>contoso.com</code> in its SAN</td>
<td>Point the app at a DC FQDN, or reissue with the extra SAN (Step 10)</td>
</tr>
<tr>
<td>The app uses an alias or load-balancer name</td>
<td>That name isn't in any DC's certificate</td>
<td>Same choice</td>
</tr>
<tr>
<td>The app uses an IP address</td>
<td>A certificate issued to DNS names won't match an IP address</td>
<td>Use a name</td>
</tr>
<tr>
<td><code>Test-Certificate</code> passes for the name used</td>
<td>The name isn't the problem</td>
<td>Step 7 if the failure is on 389, or back to Step 5 for the app's own trust store</td>
</tr>
</tbody></table>
<p>The Directory Services team's older post adds a caveat for multi-name certificates: "The first name in the SAN has to match the FQDN of the server." Keep the DC's own FQDN first when you reissue (our habit).</p>
<hr />
<h2>Step 7: Who is binding unsigned or in clear text? (read-only, then one low-risk logging change)</h2>
<p>This is the 389 side. Microsoft's definition of what the DC counts, from the Event 2889 text: "The following client performed a SASL (Negotiate/Kerberos/NTLM/Digest) LDAP bind without requesting signing (integrity verification), or performed a simple bind over a clear text (non-SSL/TLS-encrypted) LDAP connection."</p>
<p>The DC already logs summaries without any change:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>When it's logged (Microsoft)</th>
<th>What it tells you</th>
</tr>
</thead>
<tbody><tr>
<td>2886</td>
<td>At startup, as a reminder when the DC doesn't require signing</td>
<td>This DC accepts unsigned binds</td>
</tr>
<tr>
<td>2887</td>
<td>"a summary Event ID 2887 one time every 24 hours to indicate how many such binds occurred"</td>
<td>How many, not who</td>
</tr>
<tr>
<td>2888</td>
<td>"a summary Event ID 2888 one time every 24 hours when such bind attempts occur" on a DC that rejects them</td>
<td>How many were rejected</td>
</tr>
<tr>
<td>2889</td>
<td>One per bind, only with the diagnostic level raised</td>
<td>Client IP, port, identity, and binding type</td>
</tr>
</tbody></table>
<p>Read the summaries on every DC first:</p>
<pre><code class="language-powershell"># Read-only. Every DC: the latest LDAP signing summaries (2886, 2887, 2888) from the Directory Service log
foreach ($dc in (Get-ADDomainController -Filter *).HostName) {
  Get-WinEvent -ComputerName $dc -FilterHashtable @{ LogName = 'Directory Service'; Id = 2886, 2887, 2888 } -MaxEvents 3 -ErrorAction SilentlyContinue |
    Select-Object @{ n = 'DC'; e = { $dc } }, TimeCreated, Id, Message | Format-List
}
</code></pre>
<p>The 2887 message ends with the two counts that matter: "Number of simple binds performed without SSL/TLS" and "Number of Negotiate/Kerberos/NTLM/Digest binds performed without signing". A DC that logs 2888 instead is already rejecting them, which is the "Strong Authentication Required" side.</p>
<p>To get names, raise the LDAP Interface Events level. Microsoft: "You can enable this additional logging by setting the 16 LDAP Interface Events diagnostic setting to 2 (Basic)." and, for the cost, "it has a negative effect on domain controller performance, and it should be disabled when it is no longer needed." Microsoft's command (a change, but a reversible logging change; do it on one DC at a time, our habit):</p>
<pre><code class="language-console">:: Low-risk change, on ONE DC. Raise LDAP Interface Events to 2 (Basic) to log Event 2889 per bind (Microsoft's form)
reg query HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics /v "16 LDAP Interface Events"
Reg Add HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics /v "16 LDAP Interface Events" /t REG_DWORD /d 2
</code></pre>
<p>Leave it long enough to cover the clients' schedules (a nightly sync, a weekly report), then group what it logged:</p>
<pre><code class="language-powershell"># Read-only. On that DC: unsigned or clear-text binds in the last 24 hours, grouped by client, identity, and binding type
# (the property order IP:port, identity, binding type is our reading of the event text; check one event with Format-List first)
Get-WinEvent -FilterHashtable @{ LogName = 'Directory Service'; Id = 2889; StartTime = (Get-Date).AddDays(-1) } -ErrorAction SilentlyContinue |
  ForEach-Object {
    [pscustomobject]@{
      Client   = $_.Properties[0].Value -replace ':\d+$', ''
      Identity = $_.Properties[1].Value
      BindType = $_.Properties[2].Value
    }
  } |
  Group-Object -Property Client, Identity, BindType | Sort-Object -Property Count -Descending |
  Select-Object Count, Name | Format-Table -AutoSize
</code></pre>
<p>Microsoft's binding types in the 2889 text: "0 - SASL Bind that does not use signing" and "1 - Simple Bind that does not support signing".</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Type 1 from an appliance or app with a service account</td>
<td>A simple bind with a password on 389, in clear text</td>
<td>Move it to LDAPS on 636 (Steps 3-6 for its DC) or to a signed SASL bind (Step 10)</td>
</tr>
<tr>
<td>Type 0 from a Windows computer</td>
<td>A client or app that asks for SASL without signing; Microsoft says Windows clients request signing by default, so look at the app or a local policy set to None</td>
<td>Step 8 (client side)</td>
</tr>
<tr>
<td>Type 0 or 1 from an IP you can't place</td>
<td>Microsoft: "Identify the make, model, and type of device for each IP address cited by:" Event 2889</td>
<td>Find the owner before anything else</td>
</tr>
<tr>
<td>No 2889 after a full cycle, and 2887 counts are zero</td>
<td>Nothing on this DC binds unsigned</td>
<td>Repeat on the next DC</td>
</tr>
</tbody></table>
<p>Then turn it back down. Microsoft's revert: <code>Reg Add HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Diagnostics /v "16 LDAP Interface Events" /t REG_DWORD /d 0</code>. The diagnostic logging article: "Make sure that you reset the entries to 0 after you finish investigating the problem."</p>
<hr />
<h2>Step 8: Which signing policy is actually in effect on each DC? (read-only)</h2>
<p>Microsoft maps the <strong>Domain controller: LDAP server signing requirements</strong> policy to one registry value, <code>LDAPServerIntegrity</code> under <code>NTDS\Parameters</code>, with None = 1 and Require Signing = 2. Channel binding is <code>LdapEnforceChannelBinding</code> in the same key (Never = 0, When Supported = 1, Always = 2). Read both on every DC, plus the policy result:</p>
<pre><code class="language-console">:: Read-only. On each DC: the signing and channel binding values it holds (an error means the value isn't set)
reg query HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters /v LDAPServerIntegrity
reg query HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters /v LdapEnforceChannelBinding
:: Which GPO set them (open the report and search Security Options for "LDAP")
gpresult /scope computer /h %TEMP%\dc-rsop.html
</code></pre>
<p>Windows Server 2025 adds a second policy. Microsoft: "The Domain controller: LDAP server signing requirements enforcement policy takes precedence over the Domain controller: LDAP server signing requirements policy." Its Not Configured state isn't neutral: "Default: Not Configured, which has the same effect as Enabled (default for new deployments)." And for upgrades and mixed domains: "During an upgrade, Windows Server preserves existing signing policies to prevent disruption; if no LDAP server signing policy exists, Windows Server 2025 requires signing by default."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Only the new Windows Server 2025 DC rejects binds</td>
<td>No signing policy defined, so that DC requires signing by default; the older DCs still accept unsigned binds</td>
<td>Fix the clients from Step 7 first, then make every DC match on purpose</td>
</tr>
<tr>
<td><code>LDAPServerIntegrity</code> = 2 on some DCs, 1 on others</td>
<td>DCs in different OUs, or a GPO that didn't apply everywhere</td>
<td><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a></td>
</tr>
<tr>
<td><code>gpresult</code> shows the setting, <code>reg query</code> doesn't match</td>
<td>Policy not refreshed yet</td>
<td>Microsoft: "To apply it immediately, run gpupdate /force on your domain controllers."</td>
</tr>
<tr>
<td>A Windows client fails against a DC that requires signing</td>
<td>The client's <strong>Network security: LDAP client signing requirements</strong> may be None</td>
<td>Microsoft's client options are None, Negotiate signing, and Require signing</td>
</tr>
</tbody></table>
<p>On the client side, Microsoft's default is fine for Windows: "All currently supported versions of Windows client use LDAP signing by default as LDAP clients." And its warning for the strict setting: "Not setting one of the sides will prevent client computers from communicating with the server."</p>
<p>LDAPS doesn't care about this policy. Microsoft's policy reference: "This setting doesn't have any impact on LDAP simple bind through SSL (LDAP TCP/636)." So "Strong Authentication Required" on 389 doesn't mean LDAPS is broken, and fixing the certificate won't fix a clear-text bind on 389 (our reading).</p>
<hr />
<h2>Step 9: Is channel binding rejecting a TLS bind? (read-only)</h2>
<p>Channel binding is narrower than people expect. Microsoft: it doesn't apply to "Simple bind over TLS", to "Certificate bind over TLS", or to "Non-TLS SASL sessions". It only covers SASL (usually NTLM or Kerberos through Negotiate) inside LDAPS or STARTTLS. Microsoft's settings:</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>Name</th>
<th>Behavior (Microsoft)</th>
</tr>
</thead>
<tbody><tr>
<td>0</td>
<td>Never</td>
<td>"The server doesn't perform channel binding validation."</td>
</tr>
<tr>
<td>1</td>
<td>When supported</td>
<td>"Clients that support CBT must provide it. Clients that don't support CBT are still allowed. The server logs auditing events."</td>
</tr>
<tr>
<td>2</td>
<td>Always</td>
<td>"All applicable TLS and SASL clients must provide CBT. The server rejects authentication from clients that don't provide a valid token."</td>
</tr>
</tbody></table>
<p>Defaults, per Microsoft: on Windows Server 2025 new deployments the policy "Defaults to When supported." On Windows Server 2022 or earlier it's "Set to Never" and "The 2023-03 security updates enable CBT auditing by default on domain controllers."</p>
<pre><code class="language-powershell"># Read-only. On each DC: channel binding events from the last 7 days (3039 fail, 3040/3041 summary or reminder, 3074/3075 audit)
Get-WinEvent -FilterHashtable @{ LogName = 'Directory Service'; Id = 3039, 3040, 3041, 3074, 3075; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>Microsoft: "Events 3039, 3074, and 3075 can only be generated when Channel Binding is set to When Supported or Always." Per-client events also need the same "16 LDAP Interface Events" level of 2 from Step 7.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>3041 and 3040 only</td>
<td>Policy is Never; 3040 counts "unprotected LDAPs binds"</td>
<td>Plan When supported (Step 10)</td>
</tr>
<tr>
<td>3075 from a client</td>
<td>"The following client performed an LDAP bind over SSL/TLS and did not provide Channel Binding Information." It would be rejected under Always</td>
<td>Update or reconfigure that client before Always</td>
</tr>
<tr>
<td>3074 from a client</td>
<td>It "would have failed the channel binding token validation if the directory server was configured to enforce validation of Channel Binding Tokens."</td>
<td>Same</td>
</tr>
<tr>
<td>3039</td>
<td>A bind already failed CBT under When supported or Always</td>
<td>That client is broken now; Step 10</td>
</tr>
<tr>
<td>None of these, but TLS binds fail</td>
<td>Not channel binding; go back to Steps 3-6 (our reading)</td>
<td>Steps 3-6</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>What people try</th>
<th>Why it misleads (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Turning LDAP signing off in the Default Domain Controllers Policy</td>
<td>It hides Step 7's evidence and reopens the attack Microsoft describes: "an attacker can cause a server to make decisions that are based on forged requests from the LDAP client." Fix the client, or time-box an exception on one DC</td>
</tr>
<tr>
<td>Setting channel binding to Never</td>
<td>3039, 3074, and 3075 stop appearing because Microsoft only logs them under When supported or Always, so you lose the list of clients to fix</td>
</tr>
<tr>
<td>Deleting every old certificate on the DC</td>
<td>The certificate the DC serves may be in the NTDS store, or may be the one you need. Read Step 4 first and export what you remove</td>
</tr>
<tr>
<td>Restarting the DC after every certificate change</td>
<td>Newer DCs pick up a new certificate without a restart, and <code>renewServerCertificate</code> forces it. A restart also hides whether the old one was ever served</td>
</tr>
<tr>
<td>"It works in ldp on the DC, so LDAPS is fine"</td>
<td>The DC trusts its own CA. Microsoft's article checks the chain "on the client computer" (Step 5)</td>
</tr>
<tr>
<td>Testing only <code>Test-NetConnection</code></td>
<td>A TCP socket opens even with no certificate (Step 2)</td>
</tr>
<tr>
<td>Turning off certificate validation in the app</td>
<td>Gets a connection, removes the server-identity check TLS gives you</td>
</tr>
<tr>
<td>Pointing the app at an IP address</td>
<td>Avoids a name mismatch by failing every name check instead (Step 6)</td>
</tr>
<tr>
<td>Leaving "16 LDAP Interface Events" at 2</td>
<td>Microsoft: "it has a negative effect on domain controller performance"</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one link, then prove it</h2>
<p>Only after Steps 1-9 agree on which link failed. Write down the current state first: the Step 3 certificate list with thumbprints, the Step 4 served thumbprint, the NTDS store contents, and the Step 8 <code>reg query</code> output from every DC (our habit). Export any certificate before you remove it.</p>
<p><strong>No usable certificate, an expired one, or a missing key (Step 3).</strong> With a Microsoft Enterprise CA, Microsoft's route is <code>certlm.msc</code> → <strong>Personal</strong> → <strong>Certificates</strong> → <strong>All Tasks</strong> → <strong>Request New Certificate</strong>, then the <strong>Domain Controller</strong> template. Microsoft: "The Domain Controller template comes preconfigured with the correct properties for LDAPS, including:" Server Authentication, the FQDN in the SAN, and "Automatic renewal before expiration". If that enrollment fails, <a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> is the next read. For a missing private key, the AskDS fix runs on the computer that generated the request, not the DC that received the certificate:</p>
<pre><code class="language-console">:: Change (low risk). On the computer where the request was generated: re-link the certificate to its private key (Microsoft's AskDS form; 0 is the store index)
certutil -repairstore MY 0
</code></pre>
<p>AskDS: "If we can't locate the private key container then we will need to request a new certificate."</p>
<p><strong>A non-Microsoft CA, or a name the template doesn't cover (Steps 3 and 6).</strong> Microsoft's <code>certreq</code> request file, with a SAN section added from the <code>certreq</code> reference (DC FQDN first, our habit; names are examples):</p>
<pre><code class="language-ini">;----------------- request.inf (Microsoft's LDAPS template, plus a SAN section) -----------------
[Version]
Signature="$Windows NT$"

[NewRequest]
Subject = "CN=DC2.contoso.com"
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = False
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0

[EnhancedKeyUsageExtension]
OID=1.3.6.1.5.5.7.3.1 ; this is for Server Authentication

[Extensions]
2.5.29.17 = "{text}"
_continue_ = "DNS=DC2.contoso.com&amp;"
_continue_ = "DNS=ldap.contoso.com"
</code></pre>
<pre><code class="language-console">:: Change. On the DC: create the request, submit it to the CA, then accept the issued certificate (Microsoft's commands)
certreq -new request.inf request.req
certreq -accept certnew.cer
</code></pre>
<p><strong>The DC serves the wrong certificate (Step 4).</strong> Put the certificate you want into the AD DS service's store, which wins over the computer store. Microsoft's Event 1220 steps: Certificates snap-in → <strong>Service account</strong> → <strong>Local computer</strong> → <strong>Active Directory Domain Services</strong> → right-click <strong>NTDS\Personal</strong> → <strong>All Tasks</strong> → <strong>Import</strong>. Then make the DC reload without a restart. Microsoft: "A new rootDse operation that's named renewServerCertificate can be used to manually trigger AD DS to update its SSL certificates without having to restart AD DS or restart the domain controller." The LDIF from Microsoft's protocol spec, saved as <code>renew.ldf</code>:</p>
<pre><code class="language-text">dn:
changetype: modify
add: renewServerCertificate
renewServerCertificate: 1
-
</code></pre>
<pre><code class="language-console">:: Change (low risk). Import the rootDSE modify against the DC that should reload (-s is ours; the LDIF is Microsoft's)
ldifde -i -f renew.ldf -s DC2.contoso.com
</code></pre>
<p>Per the protocol spec, the account running it needs the Reload-SSL-Certificate control access right on that DC's nTDSDSA object. Microsoft also notes AD DS "detects when a new certificate is dropped into its certificate store and then triggers an SSL certificate update" by itself; the LDIF just forces it now.</p>
<p><strong>The client doesn't trust the chain (Step 5).</strong> Add your root to the client's Trusted Root store and the issuing CA to Intermediate (Group Policy for Windows clients; the vendor's certificate import for an appliance). For one Windows test box, <code>certutil</code> can do it directly:</p>
<pre><code class="language-console">:: Change. On ONE Windows test client: add the root and the issuing CA (file names are examples)
certutil -addstore Root ContosoRootCA.cer
certutil -addstore CA ContosoIssuingCA.cer
</code></pre>
<p>If the chain is fine and only revocation fails, fix the CDP instead (<a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline</a>).</p>
<p><strong>Unsigned or clear-text binds (Steps 7 and 8).</strong> Fix the client from Step 7's list, not the DC. Microsoft: "It's also quite possible that a solution for both topics consists in a single change. For example, by moving from simple bind to SASL using Kerberos or TLS with simple bind." For the badge server, that's its LDAP setting moved to port 636 with your CA imported (Steps 3-6 for the DC it uses). Then require signing on purpose, on every DC: <strong>Domain controller: LDAP server signing requirements</strong> = <strong>Require signing</strong> in the Default Domain Controllers Policy. On Windows Server 2025, leave <strong>Domain controller: LDAP server signing requirements enforcement</strong> at its default. If one application can't be fixed this week, a time-boxed exception is the enforcement policy set to Disabled with the older policy at None. Microsoft: "Disabled: The Domain controller: LDAP server signing requirements policy determines the LDAP signing requirement." Put an end date on the ticket (our habit).</p>
<p><strong>Channel binding (Step 9).</strong> Start at When supported. Microsoft: "To maximize compatibility with older operating system versions (Windows Server 2008 and earlier versions), we recommend that you enable this setting with a value of 1." Move to Always only after 3039, 3074, and 3075 stay quiet on every DC. Use the <strong>Domain controller: LDAP server channel binding token requirements</strong> policy. If you test with the registry value on one DC first, Microsoft says the change is live without a restart: "LDAP server responds dynamically to changes to this registry entry."</p>
<p><strong>Then prove it.</strong> For LDAPS, from the client side: re-run Step 4's served-certificate read (the new thumbprint), Step 5's <code>certutil -verify</code>, then Microsoft's ldp test. Microsoft: "Ensure that Port is set to 636, the Connectionless check box is cleared, and the SSL check box is selected, and then click OK." Then "RootDSE information should print in the right pane, indicating a successful connection." For signing, Microsoft's ldp simple-bind test on 389 carries its own warning: "Run it only in an isolated lab, use a nonprivileged disposable test account, and never enter production credentials." In production, Microsoft's check: "For production validation, monitor Event Viewer for Event ID 2888." The pass is the application working against every DC it can reach, no new Event 2889 from its IP, and Event 1220 gone from the DC (our reading). Then set "16 LDAP Interface Events" back to 0.</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>No usable certificate on the DC</td>
<td>Domain Controller template enrollment, or a <code>certreq</code> request with Server Authentication</td>
<td>A self-signed certificate on a DC</td>
</tr>
<tr>
<td>Wrong certificate served</td>
<td>Intended certificate in NTDS\Personal, then <code>renewServerCertificate</code></td>
<td>Deleting every certificate in the computer store</td>
</tr>
<tr>
<td>Client doesn't trust the chain</td>
<td>Root and intermediate on the client, by GPO or vendor import</td>
<td>Disabling certificate validation in the app</td>
</tr>
<tr>
<td>Name not in the certificate</td>
<td>The app pointed at a DC FQDN, or a reissue with the extra SAN</td>
<td>Connecting by IP address</td>
</tr>
<tr>
<td>Appliance sends simple binds on 389</td>
<td>The appliance moved to LDAPS, then Require signing</td>
<td>Signing off in the Default Domain Controllers Policy</td>
</tr>
<tr>
<td>Only the Windows Server 2025 DC rejects binds</td>
<td>Fix the clients, then make every DC require signing</td>
<td>Disabling the 2025 enforcement policy with no end date</td>
</tr>
<tr>
<td>Clients logged in 3074 or 3075</td>
<td>When supported until they're fixed, then Always</td>
<td>Channel binding set to Never</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why is LDAPS not working on my domain controller?</strong>
Most often the DC has no certificate it can use (Event 1220), serves a different certificate than the one you renewed, or serves one the client doesn't trust or can't match to the name it used. Microsoft: "There's no user interface for configuring LDAPS." A valid certificate is what turns it on.</p>
<p><strong>What is Event ID 2889?</strong>
A per-bind record of an unsigned SASL bind or a clear-text simple bind on a DC, with the client IP, the identity, and the binding type (0 for SASL without signing, 1 for a simple bind). It only appears after "16 LDAP Interface Events" is set to 2; Event 2887 is the daily count that's logged without it.</p>
<p><strong>What's the difference between LDAP signing and LDAPS?</strong>
Microsoft: "LDAP signing provides integrity and authenticity but not confidentiality." LDAPS (TLS on 636) encrypts the whole session, and Microsoft says signing "applies only to non-TLS SASL sessions." A simple bind needs TLS to protect the password.</p>
<p><strong>Do I need to restart the domain controller after replacing the LDAPS certificate?</strong>
Usually not on current versions. Microsoft: AD DS detects a new certificate in its store, and <code>renewServerCertificate</code> triggers the update "without having to restart AD DS or restart the domain controller." Microsoft's enrollment steps still say to restart after the first certificate, so a restart in a maintenance window is the fallback.</p>
<p><strong>What does "Strong Authentication Required" mean?</strong>
The DC requires signing and the client sent a simple bind in clear text or an unsigned SASL bind. Microsoft's ldp test shows it as "Ldap_simple_bind_s() failed: Strong Authentication Required". Find the client in Event 2889 (Step 7) and move it to LDAPS or a signed bind.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> (when the DC never gets its Domain Controller certificate)</li>
<li><a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline 0x80092013</a> (when the client can't check the DC certificate's CRL)</li>
<li><a href="https://dev.to/adminpackstudio/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage-26i6">NPS Event 6273 Reason Code 16 or 22</a> (the same server-certificate checks for PEAP on NPS)</li>
<li><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> (when the signing policy reaches some DCs and not others)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (a wrong clock makes a valid certificate look expired)</li>
</ul>
<h2>Free checklist</h2>
<p>Requiring LDAP signing before anyone read Event 2889 is enforcement before audit, and so is turning it back off domain-wide when one badge server breaks. A Conditional Access policy enforced before anyone read the report-only results is the same mistake. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=ldaps-not-working-domain-controller">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=ldaps-not-working-domain-controller</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=ldaps-not-working-domain-controller">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=ldaps-not-working-domain-controller</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>Test-NetConnection</code>, <code>Get-NetTCPConnection</code>, <code>Get-WinEvent</code>, <code>Get-ChildItem Cert:\LocalMachine\My</code>, <code>certutil -verifystore</code>, <code>-store</code>, and <code>-verify -urlfetch</code>, the .NET SslStream read of the served certificate, <code>Test-Certificate</code>, <code>Resolve-DnsName</code>, <code>Get-ADDomainController</code>, <code>reg query</code>, <code>gpresult</code>, and <code>ldp.exe</code> connects. Raising and lowering "16 LDAP Interface Events", requesting, accepting, importing, or repairing certificates, <code>renewServerCertificate</code>, adding CA certificates to client stores, and changing LDAP signing, signing enforcement, or channel binding policy are deliberate changes. Change one link at a time, test from the failing client against every DC it can reach, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[The Target Principal Name Is Incorrect: A Read-First DNS, DC Password, SPN, and Kerberos Event 4 Triage]]></title><description><![CDATA[The target principal name is incorrect? Read which DC was reached, the KDC's password copy, duplicate SPNs, and Event 4 before resetting.]]></description><link>https://adminpackstudio.hashnode.dev/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/the-target-principal-name-is-incorrect-a-read-first-dns-dc-password-spn-and-kerberos-event-4-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Mon, 19 Oct 2026 00:42:00 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "BR-DC2 came back on Friday after the branch was offline for most of a month. Now replication from it fails with 'The target principal name is incorrect', a file server we reach through an old alias says 'target account name is incorrect', and the old DC we're retiring won't demote. A forum post says to stop the KDC on every DC, force-remove the old DC, and rejoin the file server. Can we start with that?"</p>
<p>Not yet. <strong>"The target principal name is incorrect" doesn't mean Active Directory is corrupt. It means a Kerberos ticket reached a computer that couldn't decrypt it: the wrong computer, the right computer holding a different password than the KDC used, or a name (SPN) registered on the wrong account.</strong> Microsoft's replication article says the error "occurs when the source domain controller doesn't decrypt the service ticket provided by the destination (target) domain controller", and that the -2146893022 (0x80090322, SEC_E_WRONG_PRINCIPAL) "error code isn't an Active Directory error." Its top cause: "the KDC has an old version of the password for the source domain controller." Stopping the KDC on every DC, forcing a demotion, or rejoining a server can each hide which of those three it was, and a forced demotion leaves metadata behind that you then have to clean up by hand. Work through it as a chain: <strong>which computers, which name, since when</strong> → <strong>where is the error reported (repadmin, dcdiag, Kerberos Event 4, KDC Event 11)?</strong> → <strong>did the traffic reach the computer you meant (DNS, hosts file, NTDS Settings GUID)?</strong> → <strong>which KDC issued the ticket, and does it have the current password for the target?</strong> → <strong>are the target's SPNs present, and on only one account?</strong> → <strong>what does the client's Event 4 name as the target and the server that answered?</strong> → <strong>is an alias (CNAME) involved?</strong> → <strong>are time and the secure channel in order?</strong> → <strong>is this a demotion?</strong> <code>repadmin</code>, <code>dcdiag</code>, <code>nltest</code>, <code>setspn</code>, <code>Get-WinEvent</code>, <code>Resolve-DnsName</code>, and <code>Get-ADComputer</code> are the sources for that chain. Stop the KDC everywhere and you'll be finding out later which DC was handing out the stale ticket.</p>
<p>If replication fails with 1722 (the RPC server is unavailable) instead, no connection was made yet, and <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">AD Replication Error 1722</a> starts earlier in the chain. If the message ends in "Cannot generate SSPI context" from SQL Server, <a href="https://dev.to/adminpackstudio/sql-server-error-18456-login-failed-for-user-a-read-first-error-log-state-auth-mode-and-login-b3n">SQL Server Error 18456</a> covers SQL SPNs in depth. This post covers <strong>"Active Directory replication, a domain controller, a demotion, or a file server fails with 'The target principal name is incorrect' (-2146893022, 0x80090322), 'target account name is incorrect', or Kerberos Event 4 KRB_AP_ERR_MODIFIED, and you need a read-first path before you stop KDCs, reset DC passwords, delete SPNs, force a demotion, or rejoin servers."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>It's a decryption failure, not a network failure.</strong> Microsoft: "Kerberos errors that are mapped by Windows code to -2146893022" include KRB_AP_ERR_MODIFIED, KRB_AP_ERR_BADMATCH, and KRB_AP_ERR_NOT_US. The connection worked; the ticket didn't (Step 2).</li>
<li><strong>Check you reached the right computer first.</strong> Microsoft's first specific cause: "A bad name-to-IP mapping in DNS, WINS, HOST, or LMHOST file." (Step 3).</li>
<li><strong>Then the password the KDC used.</strong> Microsoft: "Replication failures can prevent domain controllers from having a current password value for domain controllers in a given domain." (Step 4).</li>
<li><strong>SPNs have to be on one account.</strong> Microsoft: "Unless a service uses the computer account and the HOST SPN, SPNs must be unique in the AD DS forest." (Step 5).</li>
<li><strong>Event 4 names both sides.</strong> Microsoft's Event 4 text: "Please ensure that the target SPN is registered on, and only registered on, the account used by the server." (Step 6).</li>
<li><strong>Forced demotion is a last resort.</strong> Microsoft: "Forced demotion leaves orphaned metadata in Active Directory on the remaining domain controllers in the forest." (Step 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which computers, which name was used, which direction, since when</td>
<td>"AD is broken" with no partner named</td>
</tr>
<tr>
<td>2. Where it's reported</td>
<td>You know whether it's replication, a member's Event 4, or a KDC event</td>
<td>Fixing replication when only one file server fails</td>
</tr>
<tr>
<td>3. Name-to-IP</td>
<td>The name resolves to the computer you meant, on every DNS server the caller uses</td>
<td>A stale A record, a hosts entry, or a GUID CNAME pointing at another DC</td>
</tr>
<tr>
<td>4. KDC and password</td>
<td>The KDC that issued the ticket has the target's current password</td>
<td>DC back from a long outage or a restore; password versions differ</td>
</tr>
<tr>
<td>5. SPNs</td>
<td>The target's SPNs exist and sit on one account</td>
<td>KDC Event 11, <code>setspn -X</code> finds duplicates</td>
</tr>
<tr>
<td>6. Event 4 read</td>
<td>You know the SPN asked for and the server that answered</td>
<td>Event 4 read as "Kerberos is broken"</td>
</tr>
<tr>
<td>7. Aliases</td>
<td>Clients use a name the server has SPNs for</td>
<td><code>\\alias\share</code> fails, <code>\\server\share</code> works</td>
</tr>
<tr>
<td>8. Time and channel</td>
<td>Clocks within five minutes; DC secure channels healthy</td>
<td>KERB_AP_ERR_SKEW, Netlogon 3210 or 5722</td>
</tr>
<tr>
<td>9. Demotion</td>
<td>Graceful demotion with replication working</td>
<td>Demotion fails, forced removal tempting</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one link, one change</td>
<td>KDC stopped on every DC, same error</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which two computers</strong></td>
<td>For replication: the destination (the DC pulling changes) and the source. Microsoft: "The destination domain controller is the domain controller requesting changes." For a share: the client and the server</td>
</tr>
<tr>
<td><strong>Which name was used</strong></td>
<td>NetBIOS name, FQDN, IP address, or an alias. A name the server has no SPN for behaves differently from its real name (Step 7)</td>
</tr>
<tr>
<td><strong>One pair or many</strong></td>
<td>One source failing for every partner points at the source (its password, its DNS records); one destination failing for every source points at the destination's DNS or KDC (our reading)</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>A DC back from a long outage, a DC restored from backup, a rename, a new alias, a new service account, a promotion or demotion, a DNS cleanup</td>
</tr>
<tr>
<td><strong>Exact error text and code</strong></td>
<td>-2146893022 (0x80090322), "Access is denied", 1722, and 1753 lead to different steps</td>
</tr>
<tr>
<td><strong>Which domains</strong></td>
<td>Microsoft's Event 4 text warns about "identically named server accounts in these two domains"</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Replication from one source fails with -2146893022 for every partner</td>
<td>The source's password differs between the KDC and the source</td>
<td>Step 4</td>
</tr>
<tr>
<td>One destination fails from several sources</td>
<td>That destination's DNS servers or hosts file, or its KDC</td>
<td>Steps 3 and 4</td>
</tr>
<tr>
<td>Error began right after a DC came back from a long outage or a restore</td>
<td>Password versions out of step</td>
<td>Step 4</td>
</tr>
<tr>
<td>KDC Event 11 on DCs</td>
<td>A duplicate SPN</td>
<td>Step 5</td>
</tr>
<tr>
<td>Kerberos Event 4 on clients reaching one server</td>
<td>SPN on another account, or that server's password</td>
<td>Step 6</td>
</tr>
<tr>
<td><code>\\alias\share</code> fails, <code>\\server\share</code> works</td>
<td>No SPN for the alias</td>
<td>Step 7</td>
</tr>
<tr>
<td>Works by IP address, fails by name</td>
<td>Microsoft notes for one scenario: "However, if you use the IP address, the command may succeed." Using an IP usually leaves Kerberos out</td>
<td>Steps 3 and 4</td>
</tr>
<tr>
<td>Demotion fails with the error</td>
<td>Replication or the KDC on the DC being removed</td>
<td>Step 9</td>
</tr>
<tr>
<td>1722 or 1753 instead</td>
<td>A connection or endpoint problem, not a decryption problem</td>
<td>The 1722 post</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Find where the error is reported (read-only)</h2>
<p>For replication, Microsoft lists the commands that show it: <code>REPADMIN /REPLSUM</code>, <code>/SHOWREPL</code>, <code>/SHOWREPS</code>, and <code>/SYNCALL</code>, plus DCDIAG's replications test. Start forest-wide:</p>
<pre><code class="language-console">:: Read-only. From a DC or an admin box with RSAT: every DC's inbound replication, errors only (Microsoft's repadmin forms)
repadmin /showrepl * /errorsonly
repadmin /showrepl * /csv &gt; showrepl.csv
</code></pre>
<p>Microsoft's own shortcut for reading the CSV is <code>repadmin /showrepl * /csv | convertfrom-csv | out-gridview</code>. To pull out only the rows with this error:</p>
<pre><code class="language-powershell"># Read-only. Which source and destination pairs report -2146893022 (the Select-String filter is ours)
repadmin /showrepl * /csv | Select-String -Pattern '2146893022'
</code></pre>
<p>List every pair. If one source DC appears in every row, that DC is where Step 4 starts. Microsoft's event table for this error includes NTDS KCC 1925 ("The attempt to establish a replication link for the following writable directory partition failed.") and 1308 in the Directory Service log on the destination.</p>
<p>For member servers and clients, the evidence is Kerberos Event 4. Microsoft: "The client then puts Kerberos event 4 (example below) in its System event log."</p>
<pre><code class="language-powershell"># Read-only. On the client that can't connect: Kerberos Event 4 in the System log, last 7 days
# (other providers also log an Id 4, so the provider filter is ours)
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 4; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
  Where-Object { $_.ProviderName -match 'Kerberos' } |
  Select-Object TimeCreated, ProviderName, Message | Format-List
</code></pre>
<p>On the DCs, look for duplicate names. Microsoft's archived KDC Event 11 message: "There are multiple accounts with name %1 of type %2." On Windows Server 2012 R2 and later DCs, a blocked attempt to add a duplicate is Event 2974 in the Directory Service log. Microsoft: "The event 2974 lists the value that was blocked and a list of one or more objects (up to 10) that already contain that value."</p>
<pre><code class="language-powershell"># Read-only. On each DC: KDC Event 11 (System log) and Event 2974 (Directory Service log), last 7 days
# (the provider match for the KDC is ours)
$since = (Get-Date).AddDays(-7)
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 11; StartTime = $since } -ErrorAction SilentlyContinue |
  Where-Object { $_.ProviderName -match 'KDC|Key-Distribution' } |
  Select-Object TimeCreated, Message | Format-List
Get-WinEvent -FilterHashtable @{ LogName = 'Directory Service'; Id = 2974; StartTime = $since } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Message | Format-List
</code></pre>
<p>Microsoft's Kerberos guidance says to check the Security and System logs on "The authenticating client", "The target server or service", and "The domain controller". Do all three before deciding which one is at fault.</p>
<hr />
<h2>Step 3: Did the traffic reach the computer you meant? (read-only)</h2>
<p>Microsoft: "The following situations can all cause a destination domain controller to submit Kerberos-encrypted traffic to the wrong Kerberos target:" stale NTDS Settings objects, bad name-to-IP mappings in DNS and WINS host records, and stale entries in HOST files. If DC1 asks for a ticket for DC2 but the packets land on DC3, DC3 can't decrypt a ticket made for DC2. Microsoft notes the realm in the error can even match: "Bad host-to-IP mappings could cause DC1 to connect to DC3 in the same domain."</p>
<p>How a destination DC finds its source, per Microsoft: it looks up the source's NTDS Settings objectGUID, queries DNS for "a matching DC GUIDED CNAME record", and follows that to the source's host A or AAAA record. Microsoft's check without a network trace, on the source DC first:</p>
<pre><code class="language-console">:: Read-only. On the SOURCE DC: its IP addresses, and its own DSA object GUID (top of the output; Microsoft's article uses /showreps)
ipconfig /all
repadmin /showreps
</code></pre>
<p>Then on the destination DC (Microsoft's sequence; DC1, the GUID, and the DNS server IP are examples):</p>
<pre><code class="language-console">:: Read-only. On the DESTINATION DC: which DNS servers it asks, and the source's GUID as this DC sees it
ipconfig /all
repadmin /showreps
:: Read-only. What each of those DNS servers returns for the source's GUID CNAME and host record (repeat per DNS server IP)
nslookup -type=cname 6f3f96d3-dfbf-4daf-9236-4d6da6909dd2._msdcs.contoso.com 10.0.0.10
nslookup -type=A+AAAA DC1.contoso.com 10.0.0.10
</code></pre>
<p>Microsoft: "The DSA object GUID listed for the source DC in the replication status section of the report should match the object GUID listed in the /showreps header when run on the console of the source DC." For the host records: "Check for duplicate host A records on all DNS Server IPs configured on the destination DC."</p>
<p>The same reads in PowerShell, one pass per DNS server (our form of Microsoft's nslookup steps):</p>
<pre><code class="language-powershell"># Read-only. On the destination DC. Fill in the DNS server IPs from ipconfig /all and the source's DSA object GUID
$dnsServers = '10.0.0.10', '10.0.0.11'
$guidName   = '6f3f96d3-dfbf-4daf-9236-4d6da6909dd2._msdcs.contoso.com'
foreach ($d in $dnsServers) {
  "--- DNS server $d"
  Resolve-DnsName -Name $guidName -Type CNAME -Server $d -DnsOnly -ErrorAction SilentlyContinue | Format-Table -AutoSize
  Resolve-DnsName -Name 'DC1.contoso.com' -Type A -Server $d -DnsOnly -ErrorAction SilentlyContinue | Format-Table -AutoSize
}
# The hosts file: any line naming the source DC?
Select-String -Path "$env:SystemRoot\System32\drivers\etc\hosts" -Pattern 'DC1'
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The GUIDs differ between the source and the destination's view</td>
<td>The destination holds a stale NTDS Settings object for that source (our reading of Microsoft's list)</td>
<td>Replication of the Configuration partition, or metadata cleanup if the old DC is gone (Step 9)</td>
</tr>
<tr>
<td>One DNS server returns a different IP, or two A records</td>
<td>Bad name-to-IP mapping; often a stale record that scavenging never removed (our reading)</td>
<td>Step 10, and <a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a></td>
</tr>
<tr>
<td>A hosts entry names the source</td>
<td>Microsoft: "Remove if present."</td>
<td>Step 10</td>
</tr>
<tr>
<td>The IP answers but isn't a DC</td>
<td>Microsoft: a request sent to a non-DC "returns error 1753" rather than this error</td>
<td>The 1722 post</td>
</tr>
<tr>
<td>Everything resolves to the right IP</td>
<td>The traffic reached the right DC</td>
<td>Step 4</td>
</tr>
</tbody></table>
<p>For a member server or client, the same question applies to the server name it used: does <code>Resolve-DnsName</code> for that name, against the client's DNS servers, return the server you meant? If the client can't find a DC at all, <a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> covers DC location.</p>
<hr />
<h2>Step 4: Which KDC issued the ticket, and does it have the current password? (read-only)</h2>
<p>This is Microsoft's top cause. Every DC runs a KDC, and Microsoft: "For same realm transactions, a destination domain controller favors getting Kerberos tickets from itself. However, it may get a ticket from a remote domain controller." If whichever KDC issued the ticket holds an older password for the source DC than the source itself does, the source can't decrypt it.</p>
<p>Why the passwords drift: Microsoft's netdom article says "Each Windows-based computer maintains a machine account password history that contains the current and previous passwords that are used for the account," and "If the sequence of password changes exceeds two changes, the computers involved may not be able to communicate, and you may receive error messages." A DC that was offline, or couldn't replicate, while its partners changed passwords is the usual way to get there (our reading). Microsoft's replication article: "Replication failures can prevent domain controllers from having a current password value for domain controllers in a given domain."</p>
<p>Find the KDC, close to the time of the failure:</p>
<pre><code class="language-console">:: Read-only. On the destination (or the client): which KDC does it find? /FORCE skips the cache
nltest /dsgetdc:contoso.com /kdc /force
</code></pre>
<p>Microsoft: the <code>nltest /dsgetdc</code> command "that's run at an elevated command prompt in close time proximity to a SEC_E_WRONG_PRINCIPAL error can be used to quickly identify which KDC a Kerberos client is targeting." The definitive answer is a network trace; Microsoft: "The definitive way to determine which domain controller a Kerberos client got a ticket from is to take a network trace."</p>
<p>Then compare the source DC's password attributes as the source sees them and as that KDC sees them:</p>
<pre><code class="language-console">:: Read-only. The source DC's computer account: on the source (DC1), then on the KDC the destination used (DC3). Names are examples
repadmin /showobjmeta DC1 "CN=DC1,OU=Domain Controllers,DC=contoso,DC=com"
repadmin /showobjmeta DC3 "CN=DC1,OU=Domain Controllers,DC=contoso,DC=com"
</code></pre>
<p>Microsoft: look "for obvious version number differences in password-related attributes (dBCSPwd, UnicodePWD, NtPwdHistory, PwdLastSet, lmPwdHistory)". The same comparison as a quick read across every DC:</p>
<pre><code class="language-powershell"># Read-only. PasswordLastSet for the source DC's account (DC1) as each DC currently holds it (the loop is ours)
$source = 'DC1'
foreach ($dc in (Get-ADDomainController -Filter *)) {
  Get-ADComputer -Identity $source -Server $dc.HostName -Properties PasswordLastSet |
    Select-Object @{ n = 'AskedDC'; e = { $dc.HostName } }, Name, PasswordLastSet
}
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The KDC's copy has a lower version, or an older PasswordLastSet</td>
<td>The KDC has the old password: Microsoft's top cause</td>
<td>Step 10: Microsoft's KDC sequence</td>
</tr>
<tr>
<td>Several DCs hold the old value</td>
<td>Inbound replication of the domain partition is failing to those DCs</td>
<td>Fix replication to them first (the 1722 post if that's the code)</td>
</tr>
<tr>
<td>The source itself is the odd one out (newer locally than anywhere else)</td>
<td>The source changed its password and couldn't replicate it out</td>
<td>Step 10: <code>netdom resetpwd</code> on the source</td>
</tr>
<tr>
<td>All copies match</td>
<td>The password isn't the cause</td>
<td>Step 5</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Are the target's SPNs present, and on only one account? (read-only)</h2>
<p>Microsoft: "SPNs may be missing, invalid, or duplicated due to simple replication latency, especially following promotion, or replication failures." And: "Duplicate SPNs may cause bad SPN to name mappings." Microsoft's first resolution step for the replication error is a DCDIAG test that isn't run by default:</p>
<pre><code class="language-console">:: Read-only. On the console of each source DC that fails outbound replication with this error
dcdiag /test:checksecurityerror
:: Read-only. Add the partner check (DC2 is an example)
dcdiag /test:checksecurityerror /ReplSource:DC2.contoso.com
</code></pre>
<p>Microsoft: "Run this command on the console of all source domain controllers that fail outbound replication with the SEC_E_WRONG_PRINCIPAL error." Per the dcdiag reference, the test checks that "at least one KDC is online for each domain and is reachable", that the DC's computer object has replicated to other DCs, and that the account "has the minimum required Service Principal Names (SPN) configured." With <code>/ReplSource</code>, it also checks time skew: "The time skew calculated between the servers to verify it's less than 300 seconds (5 minutes) for Kerberos."</p>
<p>Then read the SPNs directly:</p>
<pre><code class="language-console">:: Read-only. The SPNs registered on the DC's own computer account (DC1 is an example)
setspn -L DC1
:: Read-only. Which account holds a given SPN
setspn -Q HOST/DC1.contoso.com
:: Read-only. Search for duplicate SPNs, forest-wide (can take a while)
setspn -X -F
</code></pre>
<p>From Microsoft's setspn reference: <code>-L</code> "Lists the currently registered SPN for the computer.", <code>-Q</code> "Queries for any existing SPNs.", <code>-X</code> "Performs a search of duplicate SPNs.", and <code>-F</code> "Performs queries at the forest, rather than domain level." (combining <code>-X</code> with <code>-F</code> is our form). Microsoft warns: "Searching for duplicates, especially forest-wide, can take a long time and lots of memory." Run it from one admin box, not every DC at once (our habit).</p>
<p>The replication SPN in a DC's ticket request is the replication service UUID plus the source's NTDS Settings GUID, as in Microsoft's trace: <code>E3514235-4B06-11D1-AB04-00C04FC2DCD2/6f3f96d3-dfbf-4daf-9236-4d6da6909dd2/contoso.com</code>. Microsoft: "If the source domain controller's SPN doesn't exist in the realm, you receive a KDC_ERR_S_PRINCIPAL_UNKNOWN error."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>checksecurityerror</code> reports missing SPNs on a new DC</td>
<td>Microsoft: replication latency "especially following promotion"</td>
<td>Let replication finish, then re-run (our reading)</td>
</tr>
<tr>
<td><code>setspn -Q</code> returns two accounts for one SPN</td>
<td>"One SPN can be associated with only one account."</td>
<td>Step 10: remove it from the wrong one</td>
</tr>
<tr>
<td>A HOST/ or service SPN for a server sits on a user account</td>
<td>Microsoft's Event 4 cause: "When the SPN is on the wrong account in Active Directory the secret that is used is the one of the accounts the SPN is on instead of the one of the servers."</td>
<td>Step 10</td>
</tr>
<tr>
<td>Duplicates only across two forests</td>
<td>"SPNs aren't required to be unique across forests, but duplicate SPNs can cause authentication issues during cross-forest authentication."</td>
<td>Decide which forest should own it (our reading)</td>
</tr>
<tr>
<td>No duplicates, SPNs present</td>
<td>SPNs aren't the cause</td>
<td>Step 6 or Step 8</td>
</tr>
</tbody></table>
<p>Since Windows Server 2012 R2, Microsoft says DCs "block the creation of duplicate service principal names (SPN) and user principal names (UPN)", so new duplicates usually come from writes to older DCs or from before that (our reading). Microsoft's blocked-write error 8647 reads "The operation failed because SPN value provided for addition/modification isn't unique forest-wide."</p>
<hr />
<h2>Step 6: Read Kerberos Event 4 on the client (read-only)</h2>
<p>On member servers and clients, the error behind "target principal name" and "target account name" messages is usually a Kerberos Event 4 with KRB_AP_ERR_MODIFIED (our reading of Microsoft's Event 4 articles). Microsoft's Kerberos guidance: "Because more than one issue can cause this error, check for related events. For example, this string might mean that the client and target server clocks are out of sync, or that the SPN isn't unique." The current event text, from Microsoft's file-share article, names three causes:</p>
<ul>
<li>The SPN on the wrong account: "This can occur when the target server principal name (SPN) is registered on an account other than the account the target service is using."</li>
<li>A password mismatch: "This error can also happen when the target service is using a different password for the target service account than what the Kerberos Key Distribution Center (KDC) has for the target service account."</li>
<li>Two accounts with the same name: "If the server name is not fully qualified, and the target domain (DNS_prefix.dns_suffix) is different from the client domain (DNS_prefix.dns_suffix), check if there are identically named server accounts in these two domains, or use the fully-qualified name to identify the server."</li>
</ul>
<p>Read the two names in the message. Microsoft's example: "The kerberos client received a KRB_AP_ERR_MODIFIED error from the server host/machinename.childdomain.rootdomain.com. The target name used was cifs/machinename.domain.com." The first is the server that answered; the second is the SPN the client asked a ticket for (our reading).</p>
<pre><code class="language-console">:: Read-only on the directory. On the client: who holds the SPN from "The target name used was", then ask for that ticket
setspn -Q cifs/FS01.contoso.com
klist get cifs/FS01.contoso.com
klist
</code></pre>
<p><code>klist get</code> "Allows you to request a ticket to the target that is specified by the SPN." It adds a ticket to your cache; nothing changes in AD.</p>
<table>
<thead>
<tr>
<th>What the event shows</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The server that answered isn't the one in the target name</td>
<td>The name resolved to another computer</td>
<td>Step 3</td>
</tr>
<tr>
<td><code>setspn -Q</code> returns an account other than the server's</td>
<td>SPN on the wrong account (Microsoft's Event 4 cause)</td>
<td>Step 10</td>
</tr>
<tr>
<td>The server's own account holds the SPN, only once</td>
<td>The server's password differs from the KDC's copy, or the clocks differ</td>
<td>Steps 4 and 8, and for members the trust-relationship post</td>
</tr>
<tr>
<td>Short name used, two domains in the forest</td>
<td>Identically named accounts; Microsoft: "use the fully-qualified name"</td>
<td>Test with the FQDN</td>
</tr>
<tr>
<td>Windows Server 2012 R2 file server, Event 4 plus Event 1097</td>
<td>Microsoft's 2012 R2 article: "the file server cannot decrypt the ticket that was encrypted in AES256"</td>
<td>Microsoft's encryption-types policy fix, after reading the current setting</td>
</tr>
<tr>
<td>None of these</td>
<td>Microsoft: "Less commonly this is caused by network problems between client and server where the ticket is truncated."</td>
<td>A simultaneous network trace (Microsoft's guidance)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: Is an alias (CNAME) involved? (read-only)</h2>
<p>The member-server version of this error often comes from a name the server doesn't own. Microsoft's SMB article: with a CNAME, <code>\\CNAME\share_name</code> can fail with "Logon Failure: The target account name is incorrect." while the NetBIOS name and FQDN work. Microsoft's causes: "Configuration issue or SMB server service hardening issue." and "The service principal name (SPN) for the DNS alias (CNAME) record for the file share server is missing."</p>
<pre><code class="language-console">:: Read-only. On the server (or with RSAT): which names does it own, and does each have a DNS record and an SPN? (FS01 is an example)
netdom computername FS01.contoso.com /enumerate:allnames
netdom computername FS01.contoso.com /verify
setspn -L FS01
</code></pre>
<pre><code class="language-powershell"># Read-only. On the client: what the alias resolves to (files.contoso.com is an example)
Resolve-DnsName -Name 'files.contoso.com' -DnsOnly | Format-Table -AutoSize
</code></pre>
<p>From Microsoft's netdom reference, <code>/verify</code> "Checks if there's a DNS record and a service principal name (SPN) for each computer name." If the alias resolves through a CNAME and isn't in the <code>/enumerate</code> list, and no SPN for it appears in <code>setspn -L</code>, that's this case. Microsoft's fix is in Step 10. If the alias points at a cluster name or a NAS, the SPN belongs on that object's account instead (our reading).</p>
<hr />
<h2>Step 8: Are time and the DC secure channel in order? (read-only)</h2>
<p><strong>Time.</strong> Microsoft's Kerberos guidance: "The difference in time between the domain controller and the client computer or the target server must be less than five minutes." A skew failure is logged as Event 4 with KERB_AP_ERR_SKEW (Microsoft's table), not MODIFIED, but check it while you're here:</p>
<pre><code class="language-console">:: Read-only. Three samples of the offset from this DC to its partner (DC1 is an example)
w32tm /stripchart /computer:DC1.contoso.com /samples:3 /dataonly
</code></pre>
<p>If the offset is minutes, read <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> before you change any passwords.</p>
<p><strong>The DC's secure channel.</strong> Microsoft's article on this error during manual replication lists Netlogon events alongside it: 3210 ("Failed to authenticate with <code>\\DOMAINDC</code>, a Windows NT domain controller for domain DOMAIN.") and 5722 ("The session setup from the computer 1 failed to authenticate."). On a DC, don't use <code>Test-ComputerSecureChannel</code>. Microsoft: "This cmdlet only works on Domain Member computers. When you run it on Domain Controllers, it returns false positive errors. To verify and reset the secure channels for Domain Controllers, use netdom.exe or nltest.exe."</p>
<pre><code class="language-console">:: Read-only. On the DC: state of its secure channel the last time it was used, and the PDC emulator
nltest /sc_query:contoso.com
netdom query fsmo
</code></pre>
<p><code>/sc_query</code> "Reports on the state of the secure channel the last time that you used it." Leave <code>nltest /sc_verify</code> for later: Microsoft says if the channel doesn't work, "this parameter removes the existing channel, and then builds a new one." That's a change. For member servers and workstations, <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">Trust Relationship Between This Workstation and the Primary Domain Failed</a> covers the member-side secure channel and machine password.</p>
<p><strong>The machine password itself.</strong> Microsoft's Directory Services blog: "Machine account passwords as such do not expire in Active Directory." And: "It is important to remember that machine account password changes are driven by the CLIENT (computer), and not the AD." So a DC that was offline for a month doesn't come back with an expired password; it comes back possibly behind on its partners' passwords, or with its own change not yet replicated (our reading, Step 4).</p>
<p><strong>SQL Server.</strong> If the message is "The target principal name is incorrect. Cannot generate SSPI context.", that's SQL Server's version. Microsoft: "In most cases, a misconfigured Service Principal Name (SPN) causes this error." The SQL side (service account, <code>MSSQLSvc</code> SPNs, Kerberos Configuration Manager) is in <a href="https://dev.to/adminpackstudio/sql-server-error-18456-login-failed-for-user-a-read-first-error-log-state-auth-mode-and-login-b3n">SQL Server Error 18456</a>; Steps 3, 5, and 8 here still apply to the DCs behind it.</p>
<hr />
<h2>Step 9: Is this a demotion? (read-only)</h2>
<p>A demotion has to replicate its last changes out and talk to a partner, so a DC that can't authenticate to its partners fails here too (our reading). Microsoft's DC deployment troubleshooting: "The built-in logs are the most important instrument for troubleshooting issues with domain controller promotion and demotion." Read them first:</p>
<pre><code class="language-powershell"># Read-only. On the DC that won't demote: the last 60 lines of each demotion log, then search them for the error (the Select-String filter is ours)
Get-Content -Path "$env:SystemRoot\debug\dcpromoui.log" -Tail 60
Get-Content -Path "$env:SystemRoot\debug\dcpromo.log" -Tail 60 -ErrorAction SilentlyContinue
Select-String -Path "$env:SystemRoot\debug\dcpromo*.log" -Pattern 'target principal|2146893022|0x80090322'
</code></pre>
<p>Microsoft: "Examine the Dcpromoui.log and dcpromo.log for the errors shown in the output, then work backwards from them to see indications of why the failure occurred." Then run Steps 2-5 for this DC as a replication source: its outbound replication, its DNS records, its password versions, and <code>dcdiag /test:checksecurityerror</code> on its console. If SYSVOL (DFS Replication) is what's failing during the demotion, <a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication Not Working</a> covers the DFSR side.</p>
<p><strong>Forced removal.</strong> Microsoft is direct about it: "Don't select this option unless the domain controller can't contact other domain controllers and there's no reasonable way to resolve that network issue." Also: "all unreplicated changes on that domain controller, such as passwords or new user accounts, are lost forever." And: "If you forcibly demote a domain controller, you must manually perform metadata cleanup immediately."</p>
<p>A community answer in an archived TechNet forum thread on this exact failure: "I was able to get my DC to demote after stopping the KDC service. Then I reran the demotion with force removal and it completed successfully." That's a forced removal, with the metadata cleanup it requires (community, labeled). If the DC still replicates with anyone, fix the password or DNS link first (Step 10) and demote gracefully.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>What people try</th>
<th>Why it misleads (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Stopping the KDC on every DC</td>
<td>Microsoft's sequence stops it on the destination, or on the DC whose password you reset. Its netdom article allows "on all domain controllers except one" only if needed; with every KDC stopped, nobody can get a ticket</td>
</tr>
<tr>
<td>Forcing the demotion first</td>
<td>Microsoft: forced removal is only for a DC that "can't contact other domain controllers"; it loses unreplicated changes and needs metadata cleanup</td>
</tr>
<tr>
<td>Rejoining the file server</td>
<td>Rejoining resets the computer's password, but doesn't move an SPN off another account or fix a CNAME. The Event 4 cause has to be read first (Step 6)</td>
</tr>
<tr>
<td><code>setspn -A</code> to add the <em>missing</em> SPN</td>
<td>Microsoft: "you should use <code>setspn -S</code> instead because it verifies that there are no duplicate SPNs." Adding a duplicate makes Event 4 worse</td>
</tr>
<tr>
<td>Deleting SPNs from the DC's own account</td>
<td>The DC needs its own SPNs; <code>dcdiag /test:checksecurityerror</code> checks for "the minimum required Service Principal Names (SPN)"</td>
</tr>
<tr>
<td>A hosts-file entry to "pin" the partner</td>
<td>Microsoft lists stale hosts entries as a cause and says to remove them</td>
</tr>
<tr>
<td>Connecting by IP address and calling it fixed</td>
<td>Usually works because Kerberos isn't used, which hides the broken link</td>
</tr>
<tr>
<td><code>nltest /sc_verify</code> or <code>/sc_reset</code> as a check</td>
<td>Both can rebuild the channel; <code>/sc_query</code> is the read</td>
</tr>
<tr>
<td><code>klist purge</code> everywhere</td>
<td>Microsoft: "Purging tickets destroys all tickets that you have cached, so use this attribute with caution." Purge the one logon session you're testing (Step 10)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one link, then prove it</h2>
<p>Only after Steps 1-9 agree on which link failed. Write down the current state first: the <code>repadmin /showrepl * /csv</code> output from Step 2, the <code>setspn -L</code> output for every account you're about to touch, and the <code>repadmin /showobjmeta</code> pair from Step 4 (our habit). An SPN you delete by mistake is easy to put back only if you saved the list.</p>
<p><strong>Wrong name-to-IP (Step 3).</strong> Fix the record, not the client. Delete the stale or duplicate A record on the DNS server, or the hosts entry, then clear the caches on the destination. Microsoft's steps: <code>IPCONFIG /FLUSHDNS</code> to "Clear the DNS Client cache", and "Run Nbtstat -R (upper case R) to refresh the NetBIOS name cache." If scavenging should have removed the record, <a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers why it didn't.</p>
<pre><code class="language-console">:: Change (low risk). On the destination DC, after the record or hosts entry is fixed
ipconfig /flushdns
nbtstat -R
klist -li 0x3e7 purge
</code></pre>
<p>Microsoft: "Kerberos tickets for the system account that are used by Active Directory replication can be purged without a restart by using KLIST -li 0x3e7 purge."</p>
<p><strong>KDC holding an old password for the source (Step 4).</strong> Microsoft's top resolution, on the destination DC: disable its KDC so it gets a ticket from another DC, restart, replicate from the source, then put the KDC back. Microsoft: "Domain controllers can be made to use other domain controllers by stopping the KDC service on a local or remote domain controller."</p>
<pre><code class="language-powershell"># Change, on the DESTINATION DC (BR-DC2). Microsoft's sequence; dry-run the startup change first
Set-Service -Name KDC -StartupType Disabled -WhatIf
# ...restart the destination DC, then replicate from the source. Microsoft's form, run against the destination:
#    repadmin /replicate BR-DC2.contoso.com DC1.contoso.com "DC=contoso,DC=com"
Set-Service -Name KDC -StartupType Automatic -WhatIf   # Microsoft's article writes Auto; Set-Service documents Automatic
Start-Service -Name KDC -WhatIf
</code></pre>
<p>Remove <code>-WhatIf</code> when the dry run shows the service you meant. Microsoft: "If it doesn't resolve the issue, see the Resolution section for an alternative solution in which you use the netdom resetpwd command to reset the computer account password of the source domain controller."</p>
<p><strong>The source DC's own password (Step 4).</strong> Run this on the DC whose password is wrong; Microsoft: "This command must be run on the DC whose machine account password you wish to reset." Microsoft's manual-replication article resets it against the PDC emulator, so find that first. Microsoft: "If you want to reset the password for a Windows domain controller, you must stop the Kerberos Key Distribution Center service and set its startup type to Manual."</p>
<pre><code class="language-console">:: Change, on the DC whose password is wrong (DC1). PDC01 is the PDC emulator from netdom query fsmo; names are examples
netdom query fsmo
sc config KDC start= demand
net stop KDC
klist -li 0x3e7 purge
netdom resetpwd /server:PDC01 /userd:contoso\admin.user /passwordd:*
</code></pre>
<p><code>demand</code> is the <code>sc config</code> value for a manual start, from Microsoft's sc config reference. Then Microsoft: "Restart the server whose password was changed." After the restart, start the KDC and set it back to Automatic (<code>sc config KDC start= auto</code>, then <code>net start KDC</code>). If you run <code>netdom resetpwd</code> on the PDC emulator itself, point <code>/server:</code> at another healthy DC (our reading of Microsoft's "the name of the domain controller to use for setting the machine account password").</p>
<p><strong>SPN on the wrong account, or duplicated (Steps 5 and 6).</strong> Microsoft's three commands, in order:</p>
<pre><code class="language-console">:: Change. Find, remove from the wrong account, add to the right one (SPN and account names are examples)
setspn -Q cifs/FS01.contoso.com
setspn -D cifs/FS01.contoso.com OLD-FS01
setspn -S cifs/FS01.contoso.com FS01
</code></pre>
<p>Microsoft's Event 4 article runs these "using Enterprise Administrator credentials". Use <code>-S</code>, not <code>-A</code>; Microsoft says Setspn.exe has had "duplicate SPN detection built-in to it since the Windows Server 2008 release" with the <code>-S</code> option. If the right account is a service account rather than the computer, add it there instead; Microsoft: "If the SPN is assigned to any account other than the correct service account (custom account or computer account), remove the SPN from those accounts."</p>
<p><strong>Alias without an SPN (Step 7).</strong> Microsoft: "avoid using the DNS CNAME to configure the file server's alternative name." Add the alias to the server's own account:</p>
<pre><code class="language-console">:: Change, on the file server (elevated). Then verify; remove the old CNAME only after clients work by the alias (our habit)
netdom computername FS01.contoso.com /add:files.contoso.com
netdom computername FS01.contoso.com /verify
setspn -L FS01
</code></pre>
<p>Per Microsoft's Core Infrastructure blog post on aliases, the command will "register the DNS A record, register additional SPNs, and add OptionalNames registry key." If the old CNAME stays in DNS, clients may keep using it, so plan its removal (our reading).</p>
<p><strong>The demotion (Step 9).</strong> Fix the password or DNS link above, confirm the DC replicates outbound, then demote normally. A forced removal (<code>Uninstall-ADDSDomainController -ForceRemoval -DemoteOperationMasterRole</code>) is only for a DC that truly can't reach a partner. Microsoft: "Metadata cleanup is a required procedure after a forced removal of Active Directory Domain Services (AD DS)." Microsoft's console route deletes the old DC's computer object in Active Directory Users and Computers with <strong>This Domain Controller is permanently offline and can no longer be demoted using the Active Directory Domain Services Installation Wizard (DCPROMO)</strong> selected. Then delete its stale DNS records.</p>
<p><strong>Then prove it.</strong> For replication, one destination, one source, one partition. Microsoft's article on diagnosing replication lists "Time accuracy within 5 minutes to support Kerberos authentication" among its dependencies, so run the Step 8 time read again if you changed anything near the clocks.</p>
<pre><code class="language-console">:: Read-mostly. Trigger one inbound replication, then re-read status (names and partition are examples)
repadmin /replicate BR-DC2.contoso.com DC1.contoso.com "DC=contoso,DC=com"
repadmin /showrepl BR-DC2.contoso.com
dcdiag /test:checksecurityerror /ReplSource:DC1.contoso.com
</code></pre>
<p>For a member server, purge only your own tickets, connect by the same name that failed, and look for the new service ticket:</p>
<pre><code class="language-console">:: Low-risk. On the client, in the user's own session
klist purge
dir \\files.contoso.com\share
klist
</code></pre>
<p>The pass is a successful replication with no new -2146893022 in <code>/showrepl</code>, or a share that opens by the alias with a <code>cifs/</code> ticket listed by <code>klist</code> and no new Event 4 (Step 2's read). Then the next pair, or the next client, since a destination that pulled from one source can still hold a bad ticket for another (our reading).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>The source's name resolves to another computer</td>
<td>Fix the DNS record or hosts entry, flush, purge the system tickets</td>
<td>A hosts entry pinning a new IP</td>
</tr>
<tr>
<td>KDC holds an old password for the source</td>
<td>Microsoft's KDC sequence on the destination</td>
<td>Stopping the KDC on every DC</td>
</tr>
<tr>
<td>The source's own password is wrong</td>
<td><code>netdom resetpwd</code> on that DC against the PDC emulator</td>
<td>Rebuilding the DC</td>
</tr>
<tr>
<td>SPN on the wrong account or duplicated</td>
<td><code>setspn -D</code> from the wrong account, <code>setspn -S</code> on the right one</td>
<td><code>setspn -A</code>, or deleting a DC's own SPNs</td>
</tr>
<tr>
<td>Alias with no SPN</td>
<td><code>netdom computername /add</code></td>
<td><code>DisableStrictNameChecking</code> and manual SPN edits</td>
</tr>
<tr>
<td>Demotion fails while replication still works</td>
<td>Fix the link, then a graceful demotion</td>
<td>Forced removal without metadata cleanup</td>
</tr>
<tr>
<td>2012 R2 file server can't decrypt AES tickets</td>
<td>Microsoft's encryption-types policy fix for that version</td>
<td>Disabling AES across the domain</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does "The target principal name is incorrect" mean?</strong>
A computer received a Kerberos ticket it couldn't decrypt. Microsoft's top cause in AD replication: "the KDC has an old version of the password for the source domain controller." The other causes Microsoft lists are a bad name-to-IP mapping (the ticket went to the wrong computer), an SPN the KDC couldn't find or found on the wrong account, and frames modified in transit.</p>
<p><strong>What is error -2146893022 (0x80090322)?</strong>
It's SEC_E_WRONG_PRINCIPAL, the code behind "The target principal name is incorrect." Microsoft: it "isn't an Active Directory error" and can come from RPC, Kerberos, SSL, LSA, or NTLM. In replication, Microsoft's article shows it remapped from the Kerberos errors KRB_AP_ERR_MODIFIED or KRB_AP_ERR_NOT_US.</p>
<p><strong>How do I find duplicate SPNs?</strong>
<code>setspn -Q &lt;SPN&gt;</code> shows which account holds one SPN; <code>setspn -X</code> searches for duplicates (add <code>-F</code> for the whole forest). On DCs, KDC Event 11 reports multiple accounts with the same name, and Event 2974 reports a blocked attempt to add a duplicate.</p>
<p><strong>Should I stop the KDC on every domain controller?</strong>
No. Microsoft's top resolution disables the KDC on the destination DC only, restarts it, replicates, and turns it back on. Its netdom article allows stopping the KDC "on all domain controllers except one" only if needed. With none running, no one in the domain can get a ticket.</p>
<p><strong>Is this the same as SQL Server's "Cannot generate SSPI context"?</strong>
Same family. SQL Server shows "The target principal name is incorrect. Cannot generate SSPI context." and Microsoft says a misconfigured SPN causes it in most cases. Start with the SQL Server post for the service account side; the DNS, SPN, and time steps here still apply.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">AD Replication Error 1722</a> (the sibling when no connection was made: DNS, RPC ports, firewalls, NICs)</li>
<li><a href="https://dev.to/adminpackstudio/sql-server-error-18456-login-failed-for-user-a-read-first-error-log-state-auth-mode-and-login-b3n">SQL Server Error 18456</a> (SQL SPNs, service accounts, and "Cannot generate SSPI context")</li>
<li><a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> (the stale A records that send a ticket to the wrong computer)</li>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (when a computer can't find a domain controller at all)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (the five-minute Kerberos clock limit)</li>
<li><a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">Trust Relationship Between This Workstation and the Primary Domain Failed</a> (the member-side secure channel and machine password)</li>
<li><a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication Not Working</a> (SYSVOL replication, including during a demotion)</li>
</ul>
<h2>Free checklist</h2>
<p>Stopping the KDC on every domain controller before anyone read which DC issued the bad ticket is a change made everywhere at once. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=target-principal-name-is-incorrect">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=target-principal-name-is-incorrect</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=target-principal-name-is-incorrect">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=target-principal-name-is-incorrect</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>repadmin /showrepl</code>, <code>/showreps</code>, and <code>/showobjmeta</code>, <code>dcdiag /test:checksecurityerror</code>, <code>Get-WinEvent</code>, <code>ipconfig /all</code>, <code>nslookup</code>, <code>Resolve-DnsName</code>, <code>Select-String</code>, <code>nltest /dsgetdc</code> and <code>/sc_query</code>, <code>Get-ADDomainController</code>, <code>Get-ADComputer</code>, <code>setspn -L</code>, <code>-Q</code>, and <code>-X</code>, <code>klist</code>, <code>netdom computername /enumerate</code> and <code>/verify</code>, <code>netdom query fsmo</code>, <code>w32tm /stripchart</code>, and <code>Get-Content</code> on the demotion logs. <code>klist get</code> adds a ticket to your own cache. Fixing DNS records and hosts entries, flushing caches, purging tickets, disabling or stopping the KDC, <code>netdom resetpwd</code>, <code>setspn -D</code> and <code>-S</code>, <code>netdom computername /add</code>, <code>nltest /sc_verify</code> and <code>/sc_reset</code>, demotion, forced removal, and metadata cleanup are deliberate changes. Change one link at a time, test from the failing pair or client, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[NPS Event 6273 Reason Code 16 or 22: A Read-First Policy, EAP Type, Certificate, and Account Triage]]></title><description><![CDATA[NPS Event 6273 reason code 16 or 22? Read the policy, EAP type, NPS certificate, account, and certificate mapping before loosening anything.]]></description><link>https://adminpackstudio.hashnode.dev/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/nps-event-6273-reason-code-16-or-22-a-read-first-policy-eap-type-certificate-and-account-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[networking]]></category><category><![CDATA[Security]]></category><category><![CDATA[sysadmin]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 22:43:05 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since the patch weekend, about a third of the laptops can't join the CORP-SECURE Wi-Fi, and some new Always On VPN users get rejected too. The NPS server's Security log is full of Event 6273. Most say reason code 16, a few say reason code 22. The users swear their passwords are right, and half of them don't even type one. A forum post says to tick Ignore user account dial-in properties, add a policy that accepts users without validating credentials until it's sorted, and reissue the NPS certificate. Can we start with that?"</p>
<p>Not yet. <strong>Event 6273 doesn't mean NPS is broken. It means NPS received the request, ran it through its policies, and refused it, and the event itself records which policy, which EAP type, and which reason.</strong> Microsoft's Event 6273 message is "Network Policy Server denied access to a user." Reason code 16 is "Authentication failed due to a user credentials mismatch. Either the user name provided does not match an existing user account or the password was incorrect." Reason code 22 is "Network Policy Server was unable to negotiate the use of an Extensible Authentication Protocol (EAP) type with the client computer." Neither one says the password was wrong for certain. Reason 16 also shows up when a certificate can't be mapped to an account, and reason 22 shows up when the client and the policy never agreed on a method, often because of the NPS server's own certificate (our reading, Steps 4 and 6). Ignoring dial-in properties doesn't touch either of those, and an accept-without-credentials policy opens the network to everyone. Work through it as a chain: <strong>which network, which users or devices, since when</strong> → <strong>is NPS auditing on, and is it 6273 or something else?</strong> → <strong>read one 6273 end to end (policy names, authentication type, EAP type, account, reason)</strong> → <strong>reason 22: did the client and the policy agree on an EAP type, and is the NPS server certificate valid and bound?</strong> → <strong>reason 16 with passwords: is the account real, unlocked, and reachable on a DC?</strong> → <strong>reason 16 with certificates: does the certificate map to the account (strong mapping since February 2025)?</strong> → <strong>does the request reach the policy you think it does?</strong> → <strong>is the NPS server itself registered, logging, and healthy?</strong> <code>auditpol</code>, <code>Get-WinEvent</code>, the NPS accounting log, <code>Get-ADUser</code>, the certificate store, and <code>certutil</code> are the sources for that chain. Accept everyone and you'll be finding out later which devices got on while it was open.</p>
<p>If the Wi-Fi or VPN profile never reached the device, <a href="https://dev.to/adminpackstudio/intune-wi-fi-profile-not-applying-a-read-first-assignment-and-eap-triage-d1">Intune Wi-Fi Profile Not Applying</a> starts earlier in the chain. If the rejections began when a CRL expired or a CA changed, <a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline</a> reads the chain first. This post covers <strong>"NPS (Network Policy Server) on Windows Server rejects 802.1X Wi-Fi, wired 802.1X, or VPN connections with Event 6273 and reason code 16 or 22, and you need a read-first path before you loosen policies, accept unauthenticated users, roll back security updates, or reissue every certificate."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>The event names the policy that rejected the request.</strong> Microsoft's 802.1X guide: "NPS event log entries contain information about the connection attempt, including the name of the connection request policy that matched the connection attempt and the network policy that accepted or rejected the connection attempt." (Step 3).</li>
<li><strong>Password or certificate decides which reason-16 path you take.</strong> Microsoft's 6273 page: "Use the information provided in Event Viewer to determine whether the authentication method that applies to the user connection is password- or certificate-based." (Steps 5 and 6).</li>
<li><strong>Reason 22 is often the NPS server certificate.</strong> Microsoft: "In Network Policy Server (NPS) on Windows Server, the server certificate that you bind to a network policy isn't updated automatically when it expires." (Step 4).</li>
<li><strong>Certificate users rejected since early 2025 need a strong mapping.</strong> Microsoft's KB5014754 on Full Enforcement mode: "Authentication will be denied if a certificate cannot be strongly mapped." (Step 6).</li>
<li><strong>Policies are an ordered list.</strong> Microsoft: "NPS checks each connection request against the first rule in the list, then the second, and so on, until a match is found." (Step 7).</li>
<li><strong>Certificates are the usual 802.1X break.</strong> Microsoft: "Most 802.1X authentication issues are because of problems with the certificate that's used for client or server authentication." (Steps 4, 6, 9).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which network, which NAS, which users or devices, since when</td>
<td>"The Wi-Fi is broken" with no event</td>
</tr>
<tr>
<td>2. Auditing and events</td>
<td>NPS auditing on; the failure is a 6273, not 13, 18, or 6274</td>
<td>No events at all, or the wrong event</td>
</tr>
<tr>
<td>3. One 6273 read</td>
<td>Policy names, authentication type, EAP type, account, reason known</td>
<td>Reason code read, nothing else</td>
</tr>
<tr>
<td>4. EAP type agreed (22)</td>
<td>Client and policy offer the same method; NPS server certificate valid and bound</td>
<td>PEAP on clients, EAP-TLS only in the policy, or an expired NPS certificate</td>
</tr>
<tr>
<td>5. Password account (16)</td>
<td>Account exists, is enabled, unlocked, and a DC can check it</td>
<td>Locked account, stale saved password, wrong domain</td>
</tr>
<tr>
<td>6. Certificate account (16)</td>
<td>Client certificate valid, right EKU and SAN, strongly mapped</td>
<td>Certificates issued before strong mapping, rejected since the DC update</td>
</tr>
<tr>
<td>7. Policy match</td>
<td>The intended connection request and network policy match first</td>
<td>An older, broader policy higher in the list</td>
</tr>
<tr>
<td>8. NPS server</td>
<td>Registered in AD, logging works, service and extensions healthy</td>
<td>NPS not in RAS and IAS Servers, log disk full, MFA extension rejecting</td>
</tr>
<tr>
<td>9. Other reason codes</td>
<td>You know what the code in front of you means</td>
<td>16 and 22 fixes applied to a 265 or a 48</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one link, one change</td>
<td>Accept-everyone policy, same broken mapping</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which network</strong></td>
<td>One SSID, one wired switch, or the VPN points at one NAS and its RADIUS client entry in NPS</td>
</tr>
<tr>
<td><strong>Which users or devices, and how many</strong></td>
<td>One user is an account; every user on one method is the policy, the certificate, or the server</td>
</tr>
<tr>
<td><strong>User or computer authentication</strong></td>
<td>Wi-Fi often authenticates the computer first; the event's account name shows which (Step 3)</td>
</tr>
<tr>
<td><strong>Which method you expect</strong></td>
<td>PEAP-MS-CHAP v2 (password), PEAP-TLS or EAP-TLS (certificate)</td>
</tr>
<tr>
<td><strong>Which NPS servers</strong></td>
<td>Two NPS servers behind one SSID can have different certificates and policies</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>Patch night on DCs or NPS, NPS certificate renewal, new CA, Windows 11 upgrade, Intune profile change, new switch or controller</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Reason 16 for everyone on certificate authentication since DC patching</td>
<td>Certificates without a strong mapping, now enforced</td>
<td>Step 6</td>
</tr>
<tr>
<td>Reason 16 for a few users on PEAP-MS-CHAP v2</td>
<td>Wrong or stale password, locked or disabled account</td>
<td>Step 5</td>
</tr>
<tr>
<td>Reason 16 for computer accounts (host/...)</td>
<td>Machine password or account problem on those devices</td>
<td>Step 5</td>
</tr>
<tr>
<td>Reason 22 for every client since a certain date</td>
<td>NPS server certificate expired or replaced, or a policy method changed</td>
<td>Step 4</td>
</tr>
<tr>
<td>Reason 22 for one device type (Macs, phones, a new image)</td>
<td>That client offers a method or TLS version the policy doesn't</td>
<td>Step 4</td>
</tr>
<tr>
<td>Password prompts on Windows 11 after an upgrade, then 16 or 22</td>
<td>Credential Guard blocking saved MS-CHAP v2 sign-in</td>
<td>Step 5</td>
</tr>
<tr>
<td>No 6273 at all, users still fail</td>
<td>Request never reached NPS, or auditing is off</td>
<td>Step 2</td>
</tr>
<tr>
<td>A different reason code</td>
<td>Look it up first</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Confirm auditing, then find the events (read-only)</h2>
<p>Microsoft's NPS troubleshooting checklist starts with the audit policy:</p>
<pre><code class="language-console">:: Read-only. On each NPS server: is the Network Policy Server audit subcategory on?
auditpol /get /subcategory:"Network Policy Server"
</code></pre>
<p>Microsoft says auditing is on when that shows Success and Failure, or Failure. If it shows No Auditing, the fix is Microsoft's <code>auditpol /set</code> line in Step 10, and Microsoft adds: "Even if audit policy appears to be fully enabled, it sometimes helps to disable and then re-enable this setting." Microsoft's audit page notes "This subcategory generates events only if NAS or IAS role is installed on the server."</p>
<p>The events live in the Security log on the NPS server, from Microsoft-Windows-Security-Auditing (Microsoft's 6273 event details). Microsoft's checklist: "Check for events that have Event ID 6273 or 6274. Most authentication failures produce these events." And: "Check the reason codes of the authentication failure events. The reason code indicates the cause of the failure."</p>
<pre><code class="language-powershell"># Read-only. On the NPS server: the last 24 hours of NPS denials, grouped by reason code, policy, and EAP type
# (field labels from Microsoft's 6273 event template; the regex parsing is ours)
$since = (Get-Date).AddHours(-24)
$denied = Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 6273; StartTime = $since } -ErrorAction SilentlyContinue |
  ForEach-Object {
    $m = $_.Message
    [pscustomobject]@{
      Time          = $_.TimeCreated
      Account       = if ($m -match 'Account Name:\s+(.+)') { $Matches[1].Trim() }
      NetworkPolicy = if ($m -match 'Network Policy Name:\s+(.+)') { $Matches[1].Trim() }
      ProxyPolicy   = if ($m -match 'Proxy Policy Name:\s+(.+)') { $Matches[1].Trim() }
      AuthType      = if ($m -match 'Authentication Type:\s+(.+)') { $Matches[1].Trim() }
      EapType       = if ($m -match 'EAP Type:\s+(.+)') { $Matches[1].Trim() }
      RadiusClient  = if ($m -match 'Client Friendly Name:\s+(.+)') { $Matches[1].Trim() }
      ReasonCode    = if ($m -match 'Reason Code:\s+(\d+)') { [int]$Matches[1] }
    }
  }
$denied | Group-Object ReasonCode, NetworkPolicy, EapType -NoElement | Sort-Object Count -Descending | Format-Table -AutoSize
</code></pre>
<p><code>Account Name</code> appears twice in the event (user and client machine); the first match is the user section (our reading of Microsoft's template). If one reason and one policy dominate, that's your chain. If the codes are spread out, take one user at a time.</p>
<p>The neighbors of 6273, each from Microsoft's audit page or NPS troubleshooting guidance:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Microsoft's text</th>
<th>What it tells you (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>6272</td>
<td>"Network Policy Server granted access to a user."</td>
<td>The success to compare against (Step 10)</td>
</tr>
<tr>
<td>6273</td>
<td>"Network Policy Server denied access to a user."</td>
<td>This post</td>
</tr>
<tr>
<td>6274</td>
<td>"Network Policy Server discarded the request for a user."</td>
<td>Microsoft lists it with 6273 for most failures; read its reason code the same way</td>
</tr>
<tr>
<td>6279</td>
<td>"Network Policy Server locked the user account due to repeated failed authentication attempts."</td>
<td>NPS's own remote access lockout (reason 64, Step 9)</td>
</tr>
<tr>
<td>13</td>
<td>A RADIUS message from an invalid RADIUS client IP</td>
<td>Microsoft: "The NPS event log records this event when the NPS server receives a message from a radius client that isn't on the configured list of radius clients." No 6273 is written for those</td>
</tr>
<tr>
<td>18</td>
<td>Message authenticator not valid</td>
<td>Microsoft: "The NPS event log records this event when authentication fails because the shared secret key of the radius client doesn't match the shared secret key of the NPS server."</td>
</tr>
</tbody></table>
<p>Events 13 and 18 are in the System log (Microsoft's NPS troubleshooting guidance lists them as NPS events; the log is our reading). If neither 6273 nor 13 nor 18 appears while users fail, the request may not be reaching this server. Microsoft: "By default, NPS sends and receives RADIUS traffic by using User Datagram Protocol (UDP) ports 1812, 1813, 1645, and 1646."</p>
<pre><code class="language-powershell"># Read-only. On the NPS server: is NPS listening on the RADIUS ports, and which NPS servers does the NAS point at?
Get-NetUDPEndpoint -LocalPort 1812, 1813, 1645, 1646 -ErrorAction SilentlyContinue |
  Select-Object LocalAddress, LocalPort, OwningProcess
Get-NpsRadiusClient | Select-Object Name, Address, Enabled
</code></pre>
<p><code>Get-NpsRadiusClient</code> "gets Remote Authentication Dial-In User Service (RADIUS) clients." Check that the access point, controller, switch, or VPN server sending the request is in the list with the address it actually uses (Microsoft's checklist item: "The RADIUS Clients and Servers list includes the radius client in question.").</p>
<hr />
<h2>Step 3: Read one 6273 end to end (read-only)</h2>
<p>The reason code is one field out of more than twenty. Microsoft's 802.1X guide: "In the event message, scroll to the bottom, and then check the Reason Code field and the text that's associated with it." Read the rest of the event first, for one user who is failing right now:</p>
<pre><code class="language-powershell"># Read-only. On the NPS server: the newest 6273 for one account (the name is an example), full message
Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 6273 } -MaxEvents 500 |
  Where-Object { $_.Message -match 'jdoe' } |
  Select-Object -First 1 -Property TimeCreated, Message | Format-List
</code></pre>
<p>The fields, in the order Microsoft's event template lists them, and what each one decides:</p>
<table>
<thead>
<tr>
<th>Section and field</th>
<th>What to read (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>User: Account Name, Account Domain</td>
<td>A <code>DOMAIN\user</code> or UPN is a user logon; a <code>host/</code> name is the computer authenticating (Step 5). A blank or odd domain points at the client's identity setting</td>
</tr>
<tr>
<td>Client Machine: Calling Station Identifier</td>
<td>Microsoft: "In 802.1x authentications the MAC Address is typically populated and can be matched from the client." It ties the event to one device</td>
</tr>
<tr>
<td>NAS: NAS IPv4 Address, NAS Identifier, NAS Port-Type</td>
<td>Which access point, switch, or VPN server sent it, and the media type the policy's NAS Port Type condition compares</td>
</tr>
<tr>
<td>RADIUS Client: Client Friendly Name, Client IP Address</td>
<td>The RADIUS client entry that matched (Step 2)</td>
</tr>
<tr>
<td>Authentication Details: Proxy Policy Name</td>
<td>The connection request policy that matched (Step 7)</td>
</tr>
<tr>
<td>Authentication Details: Network Policy Name</td>
<td>The network policy that rejected it. Not the one you expected means Step 7 first</td>
</tr>
<tr>
<td>Authentication Details: Authentication Type, EAP Type</td>
<td>What the client actually used: PEAP or EAP, and the inner type. Compare with Step 4</td>
</tr>
<tr>
<td>Authentication Details: Authentication Server</td>
<td>Which server did the authentication: this NPS, or a remote RADIUS server if the request was forwarded (Step 7)</td>
</tr>
<tr>
<td>Reason Code, Reason</td>
<td>Read last, with the fields above already known</td>
</tr>
</tbody></table>
<p>Two things trip people up here. The reason text in current events can differ from Microsoft's 2008-era reason-code table: a 2024 Microsoft Q&amp;A post quotes the reason 22 text as "The client could not be authenticated because the Extensible Authentication Protocol (EAP) Type cannot be processed by the server." Same code, same meaning. And a 6273 that names an unexpected network policy is a policy-order problem before it's a credential problem, whatever the code says (our reading).</p>
<p>Then the accounting log, which records every request and response, not only failures. Microsoft's Always On VPN guide: "NPS creates and stores the NPS accounting logs." Microsoft's accounting page: "The default location is the systemroot\System32\LogFiles folder." The file names start with IN (Microsoft's Always On VPN page shows the naming).</p>
<pre><code class="language-powershell"># Read-only. On the NPS server: the newest accounting log files, then every line for one account
$logs = Get-ChildItem -Path "$env:windir\System32\LogFiles" -Filter 'IN*' | Sort-Object LastWriteTime | Select-Object -Last 2
$logs | Select-Object Name, Length, LastWriteTime
$logs | Select-String -Pattern 'jdoe' | Select-Object -Last 20 | ForEach-Object { $_.Line }
</code></pre>
<p>Microsoft's Always On VPN page lists the database-format fields, including <code>Packet-Type</code>, <code>User-Name</code>, <code>Calling-Station-ID</code>, <code>NAS-IP-Address</code>, <code>Client-Friendly-Name</code>, <code>Authentication-Type</code>, <code>Policy-Name</code>, and <code>Reason-Code</code>. If your server writes the DTS Compliant (XML) format, the same names appear as elements (our reading of Microsoft's accounting page). Microsoft on the log: "The NPS logs can help you diagnose policy-related issues." A request with no response line at all, or nothing in the log for that user, sends you back to Step 2 (our reading).</p>
<hr />
<h2>Step 4: For reason 22, did the client and the policy agree on an EAP type? (read-only)</h2>
<p>Reason 22 means the EAP conversation never got as far as the account. Three things decide it: which method the client offers, which methods the matching policy allows, and whether the NPS server can present a usable certificate for that method (our reading of Microsoft's reason-code and certificate pages).</p>
<p><strong>The policy side.</strong> Microsoft's checklist item: "The network policy constraints list the correct authentication method." In the NPS console that's the policy's <strong>Constraints</strong> tab, <strong>Authentication Methods</strong>, <strong>EAP Types</strong>. A Microsoft-moderated answer on a 2018 reason 22 thread (community): "Please make sure you have selected PEAP-MSCHAPv2; not MSCHAPv2 as a supported authentication method in your NPS policy." Microsoft's Always On VPN page describes the same mismatch from the client side, for error 812: "You typically encounter this error when the NPS specified an authentication condition the client can't meet."</p>
<p><strong>The client side.</strong> On a failing Windows client, the WLAN or Wired AutoConfig log shows the method and the reason Windows gave up. Microsoft: "On the client side, go to Event Viewer (Local)\Applications and Services Logs\Microsoft\Windows\WLAN-AutoConfig/Operational for wireless issues."</p>
<pre><code class="language-powershell"># Read-only. On a failing Windows client: the newest WLAN AutoConfig events (wired: Microsoft-Windows-Wired-AutoConfig/Operational)
Get-WinEvent -LogName 'Microsoft-Windows-WLAN-AutoConfig/Operational' -MaxEvents 30 |
  Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List
</code></pre>
<p>Read the EAP method the client used and compare it with the 6273's Authentication Type and EAP Type and the policy's list. PEAP on the client with only "Smart card or other certificate" in the policy, or EAP-TLS on the client with only PEAP in the policy, is a reason 22 by design (our reading). Microsoft also warns not to offer the same inner method both tunneled and untunneled: "When you deploy both a tunneled EAP method and EAP (which isn't protected), don't use the same authentication type."</p>
<p><strong>The NPS server certificate.</strong> PEAP-MS-CHAP v2 needs one too. Microsoft: "With PEAP-MS-CHAP v2, PEAP-TLS, or EAP-TLS as the authentication method, the NPS must use a server certificate that meets the minimum server certificate requirements." And the part that breaks things on renewal day: "When the certificate applied to a policy expires, an administrator must manually update each policy that uses certificate-based authentication to reference a new, non-expired certificate. Until you update the policy, clients that rely on that policy can't successfully authenticate." Microsoft adds that this manual step "isn't required on NPS servers configured for certificate autoenrollment."</p>
<p>Microsoft publishes a read-only way to see which certificate a policy is bound to: export the configuration and pull the thumbprint from the policy's EAP blob. Microsoft's snippet, with the policy name as the only change:</p>
<pre><code class="language-powershell"># Read-only (writes one XML file). On the NPS server: which certificate thumbprint is bound to this network policy?
# Microsoft's snippet from "Manage certificates used with NPS"; the export contains RADIUS shared secrets in clear text, so delete it after reading
$PolicyName = "CORP-SECURE Wireless" # Change this to your policy name (case-insensitive)
$NPSConfigPath = "$env:TEMP\nps-config.xml"
Export-NpsConfiguration -Path $NPSConfigPath
$blob = (([xml](Get-Content "$env:TEMP\nps-config.xml" -Raw)).SelectNodes("//RadiusProfiles//*[@name]") | Where-Object { $_.name -ieq $PolicyName }).Properties.msEAPConfiguration.'#text'
$found = [regex]::Matches($blob, '14000000([0-9a-f]{40})') | ForEach-Object { $_.Groups[1].Value.ToUpper() } | Select-Object -Unique
if (-not $found) { "No certificate thumbprint found in policy '$PolicyName'" } elseif ($found.Count -eq 1) { "Bound certificate thumbprint:$found" } else { "WARNING: multiple candidate thumbprints found, manual review of $NPSConfigPath needed:`n$($found -join "`n")" }
</code></pre>
<p>Microsoft's caution on that file: "The exported NPS configuration file contains unencrypted shared secrets for RADIUS clients and members of remote RADIUS server groups." Then compare the thumbprint with what's actually in the machine store:</p>
<pre><code class="language-powershell"># Read-only. On the NPS server: certificates that can do server authentication, with expiry and names
Get-ChildItem -Path Cert:\LocalMachine\My -SSLServerAuthentication |
  Select-Object Thumbprint, Subject, NotAfter, @{ n = 'DnsNames'; e = { $_.DnsNameList -join ', ' } }, @{ n = 'EKU'; e = { $_.EnhancedKeyUsageList.FriendlyName -join ', ' } } |
  Format-List
# Anything expiring in the next 30 days
Get-ChildItem -Path Cert:\LocalMachine\My -ExpiringInDays 30 | Select-Object Thumbprint, Subject, NotAfter
</code></pre>
<p>The bound thumbprint missing from that list, or present with a past <code>NotAfter</code>, is your reason 22. Microsoft's server certificate requirements, each checkable in that output: "The Subject name contains a value."; the certificate includes "the Server Authentication purpose in EKU extensions (the object identifier (OID) for Server Authentication is 1.3.6.1.5.5.7.3.1)"; "The Subject Alternative Name (SubjectAltName) extension, if used, must contain the DNS name of the server." And it has to be trusted by the clients: it must "Be issued by a certification authority (CA) that is trusted by client computers." A new certificate from a different CA, or a client profile that pins the old server name or root, fails here even though the certificate itself is fine (our reading of Microsoft's client-side validation rules: "The name in the Subject line of the server certificate matches the name that's configured on the client for the connection.").</p>
<p><strong>TLS settings.</strong> In the same 2018 thread, the poster found the cause in a hardening change: "I followed a document from microsoft increasing security by turning off SSL 2.0/3.0 and TLS 1.0/1.1 and only allowing TLS 1.2." That was Windows Server 2012 (community, old). On current builds, don't re-enable old protocols on that basis; read what changed under SCHANNEL and when, and compare with a server that works (our reading):</p>
<pre><code class="language-console">:: Read-only. On the NPS server: SCHANNEL protocol overrides (no output under Protocols = OS defaults)
reg query "HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols" /s
</code></pre>
<p>Clocks matter at this step too: reason 264 is "The Security Support Provider Interface (SSPI) called by EAP reports that the system clocks on the NPS server and the access client are not synchronized." See <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> for that read.</p>
<hr />
<h2>Step 5: For reason 16 with passwords, is the account real, usable, and reachable? (read-only)</h2>
<p>This step is for PEAP-MS-CHAP v2 (the event's EAP Type says so). Microsoft's NPS troubleshooting page lists three checks for 6273 reason 16: "Check that the username and password for the user are valid.", "Check to see whether the user account is locked in Active Directory.", and "Check that the request is targeted to the correct domain controller and that the user account exists."</p>
<pre><code class="language-powershell"># Read-only. Any domain-joined machine with RSAT: the account NPS was asked about (the name is an example)
Get-ADUser -Identity jdoe -Properties Enabled, LockedOut, PasswordExpired, PasswordLastSet, BadLogonCount, LastBadPasswordAttempt, AccountExpirationDate, UserPrincipalName |
  Select-Object SamAccountName, UserPrincipalName, Enabled, LockedOut, PasswordExpired, PasswordLastSet, BadLogonCount, LastBadPasswordAttempt, AccountExpirationDate
</code></pre>
<p><code>BadLogonCount</code> and <code>LastBadPasswordAttempt</code> come from the DC that answered, so read them on the DC that NPS uses (our reading). Disabled, expired, and locked accounts usually get their own codes (34, 35, 36), so a reason 16 with a healthy-looking account points at the password the client sent or the name it sent it under (our reading of Microsoft's reason-code table).</p>
<p><strong>The DC's side of the same attempt.</strong> MS-CHAP v2 is validated by the DC, so the DC's Security log should show the attempt as Event 4776 (our reading; Microsoft: "This event generates every time that a credential validation occurs using NTLM authentication." and "For domain accounts, the domain controller is authoritative.").</p>
<pre><code class="language-powershell"># Read-only. On a DC (or remotely with -ComputerName): recent 4776 entries for the account
Get-WinEvent -ComputerName DC01 -FilterHashtable @{ LogName = 'Security'; Id = 4776; StartTime = (Get-Date).AddHours(-4) } -MaxEvents 2000 |
  Where-Object { $_.Message -match 'jdoe' } |
  Select-Object -First 10 -Property TimeCreated, Message | Format-List
</code></pre>
<p>The Error Code field, from Microsoft's 4776 table:</p>
<table>
<thead>
<tr>
<th>4776 Error Code</th>
<th>Microsoft's description</th>
</tr>
</thead>
<tbody><tr>
<td>0xC0000064</td>
<td>"The username you typed does not exist. Bad username."</td>
</tr>
<tr>
<td>0xC000006A</td>
<td>"Account logon with misspelled or bad password."</td>
</tr>
<tr>
<td>0xC0000071</td>
<td>"Account logon with expired password."</td>
</tr>
<tr>
<td>0xC0000072</td>
<td>"Account logon to account disabled by administrator."</td>
</tr>
<tr>
<td>0xC0000234</td>
<td>"Account logon with account locked."</td>
</tr>
<tr>
<td>0x0</td>
<td>Success: the DC accepted the password, so look at NPS policy, not the password (our reading)</td>
</tr>
</tbody></table>
<p>No 4776 at all on any DC for that attempt means NPS didn't get as far as a DC (Step 8) or the name went somewhere else, a trusted domain or a different realm (our reading).</p>
<p><strong>Which name was sent.</strong> The 6273's Account Name and Account Domain show what the client sent. A UPN suffix NPS can't resolve, a saved credential from before a rename, or a personal device sending only <code>jdoe</code> with no domain all look like reason 16 (our reading; Microsoft's codes 7 and 8 are the explicit versions: the domain in the User-Name attribute doesn't exist, or the user account doesn't). Microsoft's best-practices page has one more: "Network Policy Server (NPS) does not support the use of the Extended ASCII characters within passwords." A few users with accented characters in new passwords is a pattern worth checking.</p>
<p><strong>Computer authentication.</strong> If the Account Name is <code>host/LAPTOP-123</code> or <code>CONTOSO\LAPTOP-123$</code>, the computer is authenticating with its machine account (our reading). Read the computer object:</p>
<pre><code class="language-powershell"># Read-only. The computer account behind a host/ authentication (the name is an example)
Get-ADComputer -Identity 'LAPTOP-123' -Properties Enabled, PasswordLastSet, whenCreated |
  Select-Object Name, Enabled, PasswordLastSet, whenCreated
</code></pre>
<p>A disabled or recently re-created computer object, or one restored from an old image, can fail where users on the same laptop succeed (our reading). <a href="https://dev.to/adminpackstudio/active-directory-account-keeps-getting-locked-out-a-read-first-event-4740-source-and-stale-13ii">AD Account Keeps Getting Locked Out</a> covers the reverse case: NPS as the caller that keeps locking an account with a saved old password.</p>
<p><strong>Windows 11 and Credential Guard.</strong> Microsoft: "Upgrades to Windows 11, version 22H2, and Windows Server 2025 have Credential Guard enabled by default unless explicitly disabled." And for Wi-Fi and VPN: "Devices that use 802.1x wireless or wired network, RDP, or VPN connections that rely on insecure protocols with password-based authentication are unable to use SSO to sign in and are forced to manually reauthenticate in every new Windows session when Credential Guard is running." Users who used to connect silently with "Automatically use my Windows logon name and password" now get a prompt, type something, and fail (our reading). Microsoft's check on the client:</p>
<pre><code class="language-powershell"># Read-only. On a failing Windows client: 1 in the output = Credential Guard is running
(Get-CimInstance -ClassName Win32_DeviceGuard -Namespace root\Microsoft\Windows\DeviceGuard).SecurityServicesRunning
</code></pre>
<p>Microsoft's direction is clear: "Microsoft recommends moving away from MSCHAPv2-based connections (for example, PEAP-MSCHAPv2 and EAP-MSCHAPv2), to certificate-based authentication (for example, PEAP-TLS or EAP-TLS). Credential Guard doesn't block certificate-based authentication."</p>
<p><strong>A pattern from the field (community).</strong> On a Microsoft Q&amp;A thread about intermittent 6273 reason 16 for wired and Wi-Fi users, one admin traced it to a read-only DC in the NPS server's site: "Appears the root cause of this problem is related to NPS trying to authenticate to an RODC in the same site." Their fix was moving the RODC to its own site. If reason 16 comes and goes for healthy accounts, check which DCs share the NPS server's site (our reading of that thread; not Microsoft documentation).</p>
<hr />
<h2>Step 6: For reason 16 with certificates, does the certificate map to the account? (read-only)</h2>
<p>This step is for EAP-TLS or PEAP-TLS. Microsoft's 6273 page: "If a certificate-based authentication method is used, examine the user or computer certificate to confirm that the user is providing the correct certificate for authentication." Reason 16's text says the user name "does not match an existing user account". With a certificate, the name comes from the certificate, so that's a mapping question (our reading).</p>
<p><strong>The certificate itself.</strong> Microsoft's client certificate requirements: "The client certificate is issued by an enterprise CA or mapped to a user or computer account in Active Directory Domain Services (AD DS)."; it carries "the Client Authentication purpose in EKU extensions (the OID for Client Authentication is 1.3.6.1.5.5.7.3.2)"; "For user certificates, the Subject Alternative Name (SubjectAltName) extension in the certificate contains the user principal name (UPN)."; and "For computer certificates, the Subject Alternative Name (SubjectAltName) extension in the certificate must contain the fully qualified domain name (FQDN) of the client, which is also called the DNS name."</p>
<pre><code class="language-powershell"># Read-only. On a failing Windows client: client-auth certificates in the user and computer stores,
# with expiry, names, and whether the AD CS SID extension (1.3.6.1.4.1.311.25.2) is present
foreach ($store in 'Cert:\CurrentUser\My', 'Cert:\LocalMachine\My') {
  Get-ChildItem -Path $store |
    Where-Object { $_.EnhancedKeyUsageList.ObjectId -contains '1.3.6.1.5.5.7.3.2' } |
    Select-Object @{ n = 'Store'; e = { $store } }, Thumbprint, Subject, Issuer, NotBefore, NotAfter,
      @{ n = 'SAN'; e = { ($_.Extensions | Where-Object { $_.Oid.Value -eq '2.5.29.17' }).Format($false) } },
      @{ n = 'SidExtension'; e = { [bool]($_.Extensions | Where-Object { $_.Oid.Value -eq '1.3.6.1.4.1.311.25.2' }) } }
}
</code></pre>
<p>The store filter, <code>Format($false)</code> and the calculated properties are our forms. More than one client-auth certificate in a store is worth noting: Richard Hicks (Microsoft MVP, his Always On VPN blog, community) answers a reader that if an older certificate that isn't strongly mapped is still in the store, "Windows could be selecting the wrong one when it connects."</p>
<p><strong>Strong mapping (the February 2025 change).</strong> Microsoft's KB5014754, on Full Enforcement mode: domain controllers "will move to Full Enforcement mode when the February 2025 Windows security update is installed. Authentication will be denied if a certificate cannot be strongly mapped." The rollback window is closed: "The option to move back to Compatibility mode will remain until the September 9, 2025 Windows security update is installed. After this date, the StrongCertificateBindingEnforcement registry key will no longer be supported." Hicks documented what that looks like for VPN: "The event log on the NPS server will also record event ID 6273 from the Microsoft Windows security auditing source with the following error message." His post shows reason code 16, and on a DC, Kdcsvc Event 39. His reading: "The above conditions indicate that a user attempted to authenticate to the VPN with a certificate that was not strongly mapped."</p>
<pre><code class="language-powershell"># Read-only. On each DC (or remotely with -ComputerName): KB5014754 mapping events from the KDC
# 39 = no strong mapping, 40 = certificate predates the account, 41 = SID mismatch (Windows Server 2008 R2 / 2008 use 41, 48, 49)
Get-WinEvent -ComputerName DC01 -FilterHashtable @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-Kerberos-Key-Distribution-Center'; Id = 39, 40, 41 } -MaxEvents 50 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>Microsoft lists the event source as Kdcsvc; the provider name in the filter is our reading (Hicks names it Kerberos-Key-Distribution-Center). If the filter returns nothing on a DC that should have events, open the System log and filter by event ID instead. Event 39's text: "The Key Distribution Center (KDC) encountered a user certificate that was valid but could not be mapped to a user in a strong way (such as via explicit mapping, key trust mapping, or a SID)." The event names the user, the certificate's subject, issuer, serial number, and thumbprint, so you can match it to the 6273.</p>
<p>What counts as strong, per Microsoft:</p>
<table>
<thead>
<tr>
<th>Mapping</th>
<th>Where it comes from</th>
<th>Strong?</th>
</tr>
</thead>
<tbody><tr>
<td>SID extension (OID 1.3.6.1.4.1.311.25.2)</td>
<td>AD CS online templates, after the May 2022 CA update</td>
<td>Yes</td>
</tr>
<tr>
<td>SAN URL with the SID tag</td>
<td>Intune SCEP (<code>OnPremisesSecurityIdentifier</code> variable) or PKCS (connector setting)</td>
<td>Yes</td>
</tr>
<tr>
<td><code>altSecurityIdentities</code> X509IssuerSerialNumber, X509SKI, X509SHA1PublicKey</td>
<td>Manual mapping on the account</td>
<td>Yes</td>
</tr>
<tr>
<td><code>altSecurityIdentities</code> X509IssuerSubject, X509SubjectOnly, X509RFC822</td>
<td>Manual mapping on the account</td>
<td>No (weak)</td>
</tr>
<tr>
<td>UPN or DNS name in the SAN only</td>
<td>Any CA</td>
<td>No (implicit, weak)</td>
</tr>
</tbody></table>
<p>The last row is our reading of the KB's rule: "In general, mapping types are considered strong if they are based on identifiers that you cannot reuse." Microsoft on the CA side: "Enterprise Certificate Authorities (CA) will start adding a new non-critical extension with Object Identifier (OID) (1.3.6.1.4.1.311.25.2) by default in all the certificates issued against online templates after you install the May 10, 2022, Windows update." And: "Environments that have non-Microsoft CA deployments will not be protected using the new SID extension after installing the May 10, 2022, Windows update."</p>
<pre><code class="language-powershell"># Read-only. The account side: explicit mappings and creation time (the user name is an example)
Get-ADUser -Identity jdoe -Properties altSecurityIdentities, whenCreated |
  Select-Object SamAccountName, whenCreated, @{ n = 'altSecurityIdentities'; e = { $_.altSecurityIdentities -join '; ' } }
</code></pre>
<p>A certificate whose <code>NotBefore</code> is older than the account's <code>whenCreated</code>, with no strong mapping, is Microsoft's "Certificate predates account" case: "If the certificate is older than the account, reissue the certificate or add a secure altSecurityIdentities mapping to the account (see Certificate mappings)."</p>
<p><strong>Intune-issued certificates.</strong> Microsoft's Intune support tip: the SCEP profile can add the SID as a SAN URI; "For device certificates, only Microsoft Entra hybrid joined devices will have SID information, so strong mapping changes are applicable only to Windows devices that are Microsoft Entra hybrid joined." And: "For other device types, like iOS or Android, strong mapping is not supported for device certificates, and user certificates should be used instead." Phones failing with device certificates while Windows laptops pass is that rule, not a bad profile (our reading). <a href="https://dev.to/adminpackstudio/intune-scep-certificate-profile-failed-a-read-first-assignment-and-ndes-triage-4ibc">Intune SCEP Certificate Profile Failed</a> covers the profile and NDES side.</p>
<p><strong>The chain.</strong> A certificate that maps fine can still fail validation on NPS. Those usually arrive as their own codes (Step 9), but read the chain from the client's exported certificate before you assume (<code>certutil -verify -urlfetch</code>; <a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline</a> walks through that output). One community report on the same Q&amp;A thread as Step 5: after KB5014754, certificates on the NPS server issued before the update were missing the SID extension, and the admin "renewed with new keys" all of them. That's one environment's finding, not Microsoft guidance.</p>
<hr />
<h2>Step 7: Does the request reach the policy you think it does? (read-only)</h2>
<p>NPS evaluates two ordered lists. First the connection request policies: "RADIUS Access-Request messages are processed or forwarded by NPS only if the settings of the incoming message match at least one of the connection request policies configured on the NPS." Then the network policies, top to bottom, first match wins (TL;DR). Three rules decide most surprises:</p>
<ul>
<li><strong>Conditions vs constraints.</strong> Microsoft: "Unlike the NPS response to unmatched conditions in the network policy, if a constraint is not matched, NPS denies the connection request without evaluating additional network policies." A broad policy higher in the list whose conditions match, but whose authentication methods don't, rejects the request before your intended policy is read (our reading of that rule).</li>
<li><strong>Disabled policies are skipped.</strong> "When you disable a network policy, NPS does not evaluate the policy when authorizing connection requests."</li>
<li><strong>Dial-in properties.</strong> "If the dial-in properties of the user account are set to deny access, the connection request is rejected by NPS." That's reason 65. And: "If NPS does not find a network policy that matches the connection request, the connection request is rejected unless the dial-in properties on the user account are set to grant access." That's reason 48.</li>
</ul>
<p>Compare the 6273's Proxy Policy Name and Network Policy Name with the policy you meant. Then read that policy's conditions against the event's fields: NAS Port Type (Microsoft's example: "if the user is only allowed to connect through a wireless access point but is attempting to connect through a virtual private network (VPN) server, access will be denied"), Windows Groups, NAS IPv4 Address, Calling or Called Station ID. To list what's configured without touching it:</p>
<pre><code class="language-powershell"># Read-only (writes one XML file with shared secrets in clear text; delete it after). On the NPS server:
# the network policy names in the exported configuration (XPath from Microsoft's certificate snippet; listing names is our form)
$path = "$env:TEMP\nps-config.xml"
Export-NpsConfiguration -Path $path
([xml](Get-Content $path -Raw)).SelectNodes("//RadiusProfiles//*[@name]") | ForEach-Object { $_.name }
Remove-Item -Path $path
</code></pre>
<p>The processing order itself is the order of the list in the NPS console under <strong>Policies</strong> (read it there). For a Windows Groups condition, read the group the way NPS will:</p>
<pre><code class="language-powershell"># Read-only. Is the account (user or computer) in the group the policy's Windows Groups condition names? (names are examples)
Get-ADGroupMember -Identity 'WiFi-Users' -Recursive | Where-Object { $_.SamAccountName -in 'jdoe', 'LAPTOP-123$' } |
  Select-Object Name, SamAccountName, objectClass
</code></pre>
<p>Wi-Fi often authenticates the computer at boot and the user at sign-in; a group condition that only contains users rejects the computer pass (our reading).</p>
<p><strong>Forwarding.</strong> If the matching connection request policy forwards to another RADIUS server, Microsoft's checklist: "The Remote Radius Server Groups list includes the radius server in question." Reason 112 says the remote server rejected it: "Check the event log on the remote RADIUS server to determine the reason that the connection request was rejected."</p>
<p><strong>MAC authentication bypass policies.</strong> Microsoft notes that Calling Station ID "is typically used for Mac Address Bypass scenarios when the connection request policy is configured for 'Accept users without validating credentials'." That's the setting behind the forum's accept-everyone suggestion. A policy like that belongs on a narrow, MAC-limited set of devices, and it must sit where your 802.1X requests can't match it (our reading).</p>
<hr />
<h2>Step 8: Is the NPS server itself registered, logging, and healthy? (read-only)</h2>
<p><strong>Registered in AD.</strong> Microsoft: "NPSs must be registered in Active Directory so that they have permission to read the dial-in properties of user accounts during the authorization process. Registering an NPS adds the server to the RAS and IAS Servers group in Active Directory." And across domains: "To provide an NPS with permission to read the dial-in properties of user accounts in Active Directory, the NPS must be registered in the domain where the accounts reside."</p>
<pre><code class="language-powershell"># Read-only. Is each NPS server in RAS and IAS Servers in the users' domain? Is the service running?
Get-ADGroupMember -Identity 'RAS and IAS Servers' | Select-Object Name, objectClass
Get-Service -Name IAS | Select-Object Name, DisplayName, Status, StartType
</code></pre>
<p><code>IAS</code> is the Network Policy Server service (Microsoft's certificate page calls it "the Network Policy Server (IAS) service").</p>
<p><strong>Logging.</strong> Microsoft's 6273 page lists unavailable log files as a cause: "If NPS is configured to record accounting information in a log file on the local computer or a remote computer, check that the hard disk is not full." And the accounting page: "To prevent the log files from filling the hard drive, it is strongly recommended that you keep them on a partition that is separate from the system partition." Reason 80 is the explicit version: NPS couldn't write accounting data.</p>
<p><strong>Domain controllers.</strong> Reasons 4, 5, and 261 are NPS failing to reach the global catalog, a DC in the user's domain, or AD DS at all. Microsoft's 6273 page: "Check that the domain controller is online." and "Check that network connections between the domain controller and NPS are working." <a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> covers a server that can't find a DC.</p>
<p><strong>Extensions (the NPS extension for Microsoft Entra MFA).</strong> Reason 21: "An IAS extension dynamic link library (DLL) that is installed on the NPS server rejected the connection request." Read what's installed before Microsoft's isolation step (Step 10):</p>
<pre><code class="language-console">:: Read-only. On the NPS server: the extension DLLs NPS loads (no values = no extensions)
reg query "HKLM\SYSTEM\CurrentControlSet\Services\AuthSrv\Parameters" /v AuthorizationDLLs
reg query "HKLM\SYSTEM\CurrentControlSet\Services\AuthSrv\Parameters" /v ExtensionDLLs
</code></pre>
<p>If the extension is there, its own log is under <strong>Applications and Services Logs &gt; Microsoft &gt; AzureMfa &gt; AuthN &gt; AuthZ</strong> (Microsoft's path).</p>
<p><strong>More than one NPS server.</strong> Access points and switches usually list two, and each NPS server writes its own Security log. Run Steps 2, 4, and 7 on each; a certificate rebound on one server and not the other gives half-the-users symptoms (our reading).</p>
<hr />
<h2>Step 9: A different reason code? Read that one instead</h2>
<p>Microsoft's reason-code tables (archived Windows Server 2008 R2 pages, still the published reference) cover 0 through 303. The ones that show up next to 16 and 22 most often, by our reading of the autocomplete data and forum threads:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Microsoft's meaning (shortened)</th>
<th>Go to</th>
</tr>
</thead>
<tbody><tr>
<td>8</td>
<td>User account in User-Name doesn't exist</td>
<td>Step 5 (name form)</td>
</tr>
<tr>
<td>21</td>
<td>An IAS extension DLL rejected the request</td>
<td>Step 8 (MFA extension)</td>
</tr>
<tr>
<td>23</td>
<td>EAP error; "Check EAP log files for EAP errors."</td>
<td>Step 4</td>
</tr>
<tr>
<td>33, 72</td>
<td>User must change password; policy doesn't allow change</td>
<td>Step 5</td>
</tr>
<tr>
<td>34, 35, 36</td>
<td>Account disabled, expired, or locked out</td>
<td>Step 5</td>
</tr>
<tr>
<td>48, 49</td>
<td>No matching network policy / connection request policy</td>
<td>Step 7</td>
</tr>
<tr>
<td>64</td>
<td>NPS Remote Access Account Lockout tripped</td>
<td>Step 10</td>
</tr>
<tr>
<td>65</td>
<td>Dial-in Network Access Permission is Deny</td>
<td>Step 7</td>
</tr>
<tr>
<td>66</td>
<td>Method not enabled on the matching policy</td>
<td>Step 4</td>
</tr>
<tr>
<td>73, 267, 283, 293, 296</td>
<td>Certificate missing the Client Authentication EKU</td>
<td>Step 6</td>
</tr>
<tr>
<td>112</td>
<td>Remote RADIUS server rejected a forwarded request</td>
<td>Step 7</td>
</tr>
<tr>
<td>256, 289</td>
<td>Certificate revoked</td>
<td>Step 6</td>
</tr>
<tr>
<td>258, 259, 291</td>
<td>CRL unreachable or CA unavailable</td>
<td><a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline</a></td>
</tr>
<tr>
<td>264</td>
<td>Clocks not synchronized</td>
<td>Step 4</td>
</tr>
<tr>
<td>265, 286, 287, 295</td>
<td>Certificate chains to a CA that NPS doesn't trust</td>
<td>Step 6</td>
</tr>
<tr>
<td>268, 278</td>
<td>Certificate expired</td>
<td>Step 6</td>
</tr>
<tr>
<td>272</td>
<td>Certificate maps to more than one account</td>
<td>Step 6</td>
</tr>
<tr>
<td>292, 298</td>
<td>User-Name doesn't match the certificate's CN or UPN</td>
<td>Step 6</td>
</tr>
</tbody></table>
<p>Microsoft: "There are intentional gaps in the numeric sequence of reason codes." A code that isn't in the tables is worth searching as its own question rather than guessing from the neighbors.</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>What people try</th>
<th>Why it misleads (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Tick Ignore user account dial-in properties</td>
<td>Microsoft: with it set, "only the settings in the network policy are used by NPS to determine whether the connection is authorized." Fixes reason 65, not 16 or 22</td>
</tr>
<tr>
<td>An accept-without-credentials policy "until it's sorted"</td>
<td>Lets any device that reaches the policy onto the network; Microsoft documents that setting for MAC bypass, not for broken 802.1X</td>
</tr>
<tr>
<td>Reissuing every certificate first</td>
<td>Reissuing without the SID extension or a strong mapping fails the same way; a new NPS certificate also has to be bound to each policy</td>
</tr>
<tr>
<td>A new self-signed NPS certificate pushed into clients' Trusted Root</td>
<td>A community answer's fix ("Problem is most likely that the self signed cert broke."); it skips the CA, and every client has to trust it</td>
</tr>
<tr>
<td>Re-enabling TLS 1.0 and SSL 3.0 on NPS</td>
<td>Based on a 2018 thread about Windows Server 2012; read what changed under SCHANNEL first (Step 4)</td>
</tr>
<tr>
<td><code>StrongCertificateBindingEnforcement</code> = 1 on DCs</td>
<td>Microsoft: no longer supported after the September 9, 2025 update</td>
</tr>
<tr>
<td>Turning off Credential Guard</td>
<td>Microsoft calls it the "more immediate, but less secure fix"; moving off MS-CHAP v2 is the recommendation</td>
</tr>
<tr>
<td>Rebooting NPS until it works</td>
<td>Clears TLS handle caches and symptoms, not the cause; the Q&amp;A thread's reboot bought "about 8-10 hours"</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one link, then prove it</h2>
<p>Only after Steps 1-9 agree on which link failed. Save the configuration first; Microsoft's best-practices page: "Save the NPS configuration with this command each time you reconfigure the NPS."</p>
<pre><code class="language-powershell"># Low-risk (writes files). On each NPS server: configuration and extension settings before any change
# The XML contains RADIUS shared secrets in clear text: keep it somewhere access-controlled
New-Item -ItemType Directory -Path 'D:\NPS-Backup' -Force | Out-Null
Export-NpsConfiguration -Path "D:\NPS-Backup\nps-config-$(Get-Date -Format yyyyMMdd-HHmm).xml"
reg export "HKLM\SYSTEM\CurrentControlSet\Services\AuthSrv\Parameters" "D:\NPS-Backup\authsrv-parameters.reg" /y
</code></pre>
<p><strong>Auditing off (Step 2).</strong> Microsoft's command, on the NPS server:</p>
<pre><code class="language-console">:: Change. Turn on success and failure auditing for NPS
auditpol /set /subcategory:"Network Policy Server" /success:enable /failure:enable
</code></pre>
<p><strong>NPS server certificate expired or replaced (Step 4).</strong> Microsoft's procedure, per policy that uses certificate-based authentication: <strong>Constraints</strong> &gt; <strong>Authentication Methods</strong> &gt; select the EAP type &gt; <strong>Edit</strong> &gt; pick the new certificate in <strong>Certificate issued to</strong> &gt; <strong>OK</strong> &gt; <strong>Apply</strong>. Repeat for every such policy, then Microsoft: "Restart the Network Policy Server (IAS) service for the change to take effect."</p>
<pre><code class="language-console">:: Change. On the NPS server, after rebinding every affected policy (Microsoft's command)
net stop ias &amp;&amp; net start ias
</code></pre>
<p>Then re-run Microsoft's thumbprint snippet from Step 4 and confirm it returns the new thumbprint. For the next renewal, Microsoft's fix for this whole class of outage: "By using autoenrollment, the server certificate is renewed automatically before it expires, and NPS continues to use the renewed certificate without administrator intervention." <a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> covers getting that working.</p>
<p><strong>EAP types don't match (Step 4).</strong> Change one side, on purpose: either the policy's EAP Types list or the client profile. For domain-joined Windows, Microsoft's best practice: "Automatically configure all of your domain member 802.1X client computers by using Group Policy." For Intune-managed devices, <a href="https://dev.to/adminpackstudio/intune-wi-fi-profile-not-applying-a-read-first-assignment-and-eap-triage-d1">Intune Wi-Fi Profile Not Applying</a> covers the profile side. Add a method to the policy only if you mean to support it; don't remove the method the working devices use (our habit).</p>
<p><strong>Password users (Step 5).</strong> Reset or unlock the one account, have the user forget the saved network and reconnect, and test. For a fleet moving to Windows 11 with Credential Guard, the durable fix is Microsoft's recommendation, certificate-based authentication, not disabling Credential Guard.</p>
<p><strong>NPS remote access lockout (reason 64).</strong> Microsoft: "To manually reset a user account that has been locked out before the failed attempts counter is automatically reset, delete the following registry subkey that corresponds to the user's account name: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RemoteAccess\Parameters\AccountLockout\domain name:user name"</p>
<p><strong>Certificates without a strong mapping (Step 6).</strong> The fix is new certificates that carry the SID, or an explicit strong mapping. Hicks: "Administrators must issue new certificates that are strongly mapped to resolve this issue." For AD CS, a re-enrollment against an online template on an updated CA picks up the SID extension (Microsoft's KB). For Intune SCEP, add the <code>OnPremisesSecurityIdentifier</code> variable as a SAN URI in a new profile; Microsoft: "Renew certificates in phases by creating a new SCEP profile and gradually targeting it to groups of users or devices." For Intune PKCS, Microsoft's order is connector update, the <code>EnableSidSecurityExtension</code> registry value set to 1, then a connector service restart, then reissue.</p>
<p>Where a certificate can't be reissued, Microsoft's manual strong mapping is X509IssuerSerialNumber in <code>altSecurityIdentities</code>. Issuer and serial are reversed from the forward format the certificate shows, and Microsoft warns: "Note that when you reverse the SerialNumber, you must keep the byte order." Microsoft's example, with a dry run added:</p>
<pre><code class="language-powershell"># Change (dry run first). Microsoft's KB5014754 example values; build your own string from the certificate's issuer and serial
Set-ADUser -Identity 'DomainUser' -Replace @{ altSecurityIdentities = 'X509:&lt;I&gt;DC=com,DC=contoso,CN=CONTOSO-DC-CA&lt;SR&gt;1200000000AC11000000002B' } -WhatIf
</code></pre>
<p><code>-Replace</code> overwrites any existing mappings on that attribute; read them first (Step 6) and include the ones you keep (our habit).</p>
<p><strong>Wrong policy matched (Step 7).</strong> Move the intended policy up, or narrow the broader policy's conditions, in the NPS console. One policy change, then a test from the same NAS and the same device type. Disable rather than delete a policy you think is unused, so it can come back (our habit).</p>
<p><strong>NPS not registered (Step 8).</strong> NPS console: <strong>NPS (Local)</strong> &gt; <strong>Register Server in Active Directory</strong>. For another domain, Microsoft's command form:</p>
<pre><code class="language-console">:: Change. Register an NPS server in another domain (domain and server names are examples)
netsh nps add registeredserver contoso.com NPS01
</code></pre>
<p><strong>MFA extension suspected (Step 8, reason 21).</strong> Microsoft's isolation step is to back up the <code>AuthSrv\Parameters</code> key (done above), delete the <code>AuthorizationDLLs</code> and <code>ExtensionDLLs</code> values, and test. Microsoft: "If NPS authentication succeeds, then the issue might be specific to Microsoft Entra multifactor authentication." While those values are gone, the server isn't asking for MFA: do it in a window, with an owner, and import the <code>.reg</code> export when the test is done (our habit).</p>
<p><strong>Then prove it.</strong> Microsoft's verification: "On a computer that is configured according to network access policy, log on to the network with a valid user account and valid credentials." Then read the success event and the policy it names:</p>
<pre><code class="language-powershell"># Read-only. On the NPS server: the newest grants and denials for the test account, side by side
Get-WinEvent -FilterHashtable @{ LogName = 'Security'; Id = 6272, 6273; StartTime = (Get-Date).AddMinutes(-30) } |
  Where-Object { $_.Message -match 'jdoe' } |
  Select-Object TimeCreated, Id, @{ n = 'Policy'; e = { if ($_.Message -match 'Network Policy Name:\s+(.+)') { $Matches[1].Trim() } } } |
  Format-Table -AutoSize
</code></pre>
<p>A 6272 naming the policy you intended is the pass. Then the next device type (another laptop model, a phone, a VPN client), because NPS caches TLS sessions: Microsoft notes "the default TLS handle expiry is 10 hours", so a device that reconnected may not have gone through the full check you just fixed (our reading).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>NPS certificate expired or replaced</td>
<td>Rebind it in every policy, restart IAS, then autoenrollment</td>
<td>A self-signed certificate pushed to clients</td>
</tr>
<tr>
<td>Client and policy offer different EAP types</td>
<td>Change the one side that's wrong</td>
<td>Enabling every method on the policy</td>
</tr>
<tr>
<td>Password users on Windows 11 prompted and failing</td>
<td>Certificate-based authentication</td>
<td>Disabling Credential Guard</td>
</tr>
<tr>
<td>Certificates without a strong mapping</td>
<td>Reissue with the SID, or an explicit strong mapping</td>
<td>Weak mappings, waiting for a rollback key</td>
</tr>
<tr>
<td>An older, broader policy matches first</td>
<td>Reorder or narrow it</td>
<td>An accept-without-credentials policy</td>
</tr>
<tr>
<td>NPS not in RAS and IAS Servers</td>
<td>Register it in each user domain</td>
<td>Adding NPS to Domain Admins</td>
</tr>
<tr>
<td>MFA extension rejecting</td>
<td>The extension's own logs, a timed isolation test</td>
<td>Leaving the DLL values deleted</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does NPS Event 6273 mean?</strong>
"Network Policy Server denied access to a user." NPS received the RADIUS request, ran it through its connection request and network policies, and rejected it. The event names the policy, the authentication and EAP types, the NAS, the account, and a reason code. Read all of them, not only the code.</p>
<p><strong>What is NPS reason code 16?</strong>
"Authentication failed due to a user credentials mismatch." With passwords (PEAP-MS-CHAP v2), check the account, the password the device saved, and the DC's Event 4776. With certificates (EAP-TLS, PEAP-TLS), check the certificate's UPN or DNS name and whether it's strongly mapped; since the February 2025 domain controller update, a weak mapping is denied.</p>
<p><strong>What is NPS reason code 22?</strong>
NPS couldn't agree on an EAP type with the client. Compare the client's method with the policy's EAP Types list, and check the NPS server certificate bound to that policy. Microsoft: it "isn't updated automatically when it expires."</p>
<p><strong>Where are the NPS logs?</strong>
The events are in the Security log on each NPS server (6272 granted, 6273 denied, 6274 discarded), also under Custom views &gt; Server roles &gt; Network Policy and Access Services. The accounting log defaults to <code>%windir%\System32\LogFiles</code>, in files whose names start with IN. Auditing must be on: <code>auditpol /get /subcategory:"Network Policy Server"</code>.</p>
<p><strong>Should I select Ignore user account dial-in properties to fix 6273?</strong>
Only if the event shows reason 65 and you mean to let the network policy alone decide access. It doesn't change how NPS checks a password or a certificate, so it doesn't fix 16 or 22.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/intune-wi-fi-profile-not-applying-a-read-first-assignment-and-eap-triage-d1">Intune Wi-Fi Profile Not Applying</a> (the client profile side: assignment, EAP settings, and the certificate the profile needs)</li>
<li><a href="https://dev.to/adminpackstudio/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage-59pf">Revocation Server Was Offline (0x80092013)</a> (when the 6273 is a revocation code, or the chain fails before mapping)</li>
<li><a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> (so the NPS server certificate and client certificates renew on their own)</li>
<li><a href="https://dev.to/adminpackstudio/intune-scep-certificate-profile-failed-a-read-first-assignment-and-ndes-triage-4ibc">Intune SCEP Certificate Profile Failed</a> (where Intune-issued client certificates, and their SID, come from)</li>
<li><a href="https://dev.to/adminpackstudio/active-directory-account-keeps-getting-locked-out-a-read-first-event-4740-source-and-stale-13ii">AD Account Keeps Getting Locked Out</a> (NPS as the caller behind repeated bad passwords)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (the clock behind reason 264)</li>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (when NPS, or any server, can't find a domain controller)</li>
</ul>
<h2>Free checklist</h2>
<p>An accept-without-credentials policy added to every SSID before one laptop was tested against the fixed certificate binding is a change nobody tested where the connections happen. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=nps-event-6273-reason-code-16-22">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=nps-event-6273-reason-code-16-22</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=nps-event-6273-reason-code-16-22">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=nps-event-6273-reason-code-16-22</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>auditpol /get</code>, <code>Get-WinEvent</code>, <code>Get-NetUDPEndpoint</code>, <code>Get-NpsRadiusClient</code>, <code>Get-ChildItem</code> on the accounting logs and certificate stores, <code>Select-String</code>, <code>Get-ADUser</code>, <code>Get-ADComputer</code>, <code>Get-ADGroupMember</code>, <code>Get-CimInstance</code>, <code>Get-Service</code>, and <code>reg query</code>. <code>Export-NpsConfiguration</code> writes a file that contains shared secrets in clear text; delete or secure it. Turning on auditing, rebinding the NPS certificate, restarting IAS, changing EAP types or policy order, account resets, <code>altSecurityIdentities</code> mappings, certificate reissue, NPS registration, removing extension DLL values, and the lockout registry key are deliberate changes. Change one link at a time, test from the failing network and device type, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[KMS Activation Error 0xC004F074: A Read-First SRV Record, Port 1688, Host, Count, and Clock Triage]]></title><description><![CDATA[KMS activation error 0xC004F074? Read slmgr /dlv, Event 12288, the _vlmcs SRV record, TCP 1688, the host, the count, and the clock first.]]></description><link>https://adminpackstudio.hashnode.dev/kms-activation-error-0xc004f074-a-read-first-srv-record-port-1688-host-count-and-clock-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/kms-activation-error-0xc004f074-a-read-first-srv-record-port-1688-host-count-and-clock-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Windows]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[dns]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 20:43:05 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since Monday, every new laptop from the latest image says Windows isn't activated, and two Windows Server 2025 VMs we built last week show error 0xC004F074. slmgr /ato says no Key Management Service (KMS) could be contacted. The old KMS server was retired over the summer and someone stood up a new one. A forum post says to point every machine at the new server with slmgr /skms, rearm them all, and if that fails, switch everything to a MAK key. Can we start with that?"</p>
<p>Not yet. <strong>0xC004F074 doesn't mean the KMS host is broken. It means every KMS host the client tried, found, or was pointed at returned an error, and the client log records each attempt.</strong> Microsoft's 0xC004F074 article lists three causes for the "No Key Management Service (KMS) could be contacted" form: "The Software Protection Platform Service (sppsvc service) on the KMS host has stopped running.", "There are networking issues between the KMS client and the KMS host server.", and "There's an incorrect or old KMS host server record in the Domain Name System (DNS)." Its other form adds "A time difference between the KMS client and the KMS host machine" and "A support version mismatch between the KMS client and the KMS host machine". A retired KMS host is the textbook case: its <code>_vlmcs._tcp</code> SRV record is still in DNS, the clients keep asking it, and the new host never published a record, or it can't activate the newer OS. Pinning every machine to one server with <code>/skms</code> turns off the discovery that would have found the next host, and rearming resets timers without fixing anything the client can't reach. Work through it as a chain: <strong>which machine, which product, which code, since when</strong> → <strong>what the client thinks it is (<code>slmgr /dlv</code>)</strong> → <strong>what the client tried (Event 12288 and 12289)</strong> → <strong>what DNS hands it (<code>_vlmcs._tcp</code>)</strong> → <strong>can it reach TCP 1688 on that host?</strong> → <strong>is the host licensed, listening, publishing, and new enough for this client?</strong> → <strong>is the count high enough?</strong> → <strong>are the clocks within four hours?</strong> → <strong>Office, AD-based activation, Azure, and other paths</strong>. <code>slmgr.vbs</code>, <code>Get-WinEvent</code>, <code>nslookup</code>, <code>Resolve-DnsName</code>, <code>Test-NetConnection</code>, <code>Get-CimInstance</code>, and <code>w32tm</code> are the sources for that chain. Pin everything with <code>/skms</code> and rearm, and you'll be undoing it one machine at a time when the next KMS host moves.</p>
<p>If domain controllers show the same DNS and port symptoms, <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> covers the DC side of the same name-and-port reads. If the stale record is one of many, <a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers why old records outlive their hosts. This post covers <strong>"Windows clients or servers with a KMS client setup key (GVLK) fail to activate with 0xC004F074, 'No Key Management Service (KMS) could be contacted', or a related KMS code, and you need a read-first path before you pin hosts, rearm, change keys, or rebuild the KMS host."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Read the client before you touch the host.</strong> <code>slmgr /dlv</code> shows whether this is even a KMS client: Microsoft says "VOLUME_KMSCLIENT indicates that the system installed the KMS Client Setup Key, or GVLK, which is the default configuration for volume license media." (Step 2).</li>
<li><strong>The Application log names the host it tried.</strong> Microsoft's 0xC004F074 fix: "Identify each event associated with the activation attempt that has an Event ID of 12288." Its Info field carries the result code and the host and port (Step 3).</li>
<li><strong>DNS finding a record isn't the client finding it.</strong> Microsoft: "If the nslookup command finds the KMS host, it does not mean that the DNS client can find the KMS host." Read the suffix search list and the record's target (Step 4).</li>
<li><strong>Test TCP 1688 from the failing client.</strong> Microsoft: "Review the TcpTestSucceeded output parameter. If it's False, it means that port 1688 is blocked between the KMS client and the KMS server." (Step 5).</li>
<li><strong>A new host has to be licensed, published, and new enough.</strong> Microsoft: "Windows Updates might be required on the KMS server to support activation of newer clients." (Step 6).</li>
<li><strong>Clocks matter.</strong> Microsoft: the related 0xC004F06C error "may occur if the difference between system time on the client computer and the system time on the KMS host is more than 4 hours." (Step 8).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which machine, product, code, network, since when</td>
<td>"Activation is broken" with no code</td>
</tr>
<tr>
<td>2. Client is a KMS client</td>
<td>GVLK installed (<code>VOLUME_KMSCLIENT</code>), right edition</td>
<td>MAK, retail, or the wrong edition's key</td>
</tr>
<tr>
<td>3. Client events read</td>
<td>Event 12288 names the host and the result</td>
<td>Nobody opened the Application log</td>
</tr>
<tr>
<td>4. DNS answers</td>
<td><code>_vlmcs._tcp</code> in the client's zone points at a live, current host</td>
<td>SRV record for the retired server</td>
</tr>
<tr>
<td>5. Port open</td>
<td>The client reaches TCP 1688 on that host</td>
<td>Works on the LAN, fails from the server VLAN or VPN</td>
</tr>
<tr>
<td>6. Host healthy</td>
<td>Licensed, sppsvc running, listening, publishing, supports this OS</td>
<td>New host never activated, or too old for Server 2025</td>
</tr>
<tr>
<td>7. Count met</td>
<td>25 for Windows clients, 5 for servers and Office</td>
<td>Count stuck at 1 after a new image</td>
</tr>
<tr>
<td>8. Time</td>
<td>Client and host within four hours (UTC)</td>
<td>VM restored from an old checkpoint</td>
</tr>
<tr>
<td>9. Office</td>
<td>Office VL pack on the host, Office client set up</td>
<td>Windows activates, Office doesn't</td>
</tr>
<tr>
<td>10. Other paths</td>
<td>AD-based activation, Azure KMS, activation type known</td>
<td>Domain clients use AD; Azure VMs can't reach Azure's KMS</td>
</tr>
<tr>
<td>11. Change</td>
<td>Only after Steps 1-10 agree: one link, one change</td>
<td><code>/skms</code> on every machine, same stale record</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which machines, and how many</strong></td>
<td>One machine is a client problem; a whole OU, VLAN, or image is DNS, the host, the count, or the image</td>
</tr>
<tr>
<td><strong>Which product</strong></td>
<td>Windows (<code>slmgr.vbs</code>) and volume Office (<code>ospp.vbs</code>) activate separately against the same host</td>
</tr>
<tr>
<td><strong>The exact code and text from <code>slmgr /ato</code></strong></td>
<td>The Settings page wording varies; the code doesn't (table below)</td>
</tr>
<tr>
<td><strong>OS edition and build</strong></td>
<td>A new OS needs a host that supports it (Step 6)</td>
</tr>
<tr>
<td><strong>Which network</strong></td>
<td>LAN, VPN, a server VLAN, Azure. Microsoft: "The KMS host must reside on your local network."</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>KMS host retired or rebuilt, DNS zone change, new image, firewall change, VM restore</td>
</tr>
</tbody></table>
<p>Run activation once, from an elevated prompt, and copy the full message. Microsoft on <code>/ato</code>: "For systems that have a Generic Volume License Key (GVLK) installed, /ato prompts a KMS activation attempt." It also says "KMS activation doesn't require elevated privileges." Elevate anyway so the rest of this post's commands work (our habit).</p>
<pre><code class="language-console">:: Low-risk (one activation attempt, which also logs Events 12288 and 12289). Copy the full result
cscript %windir%\system32\slmgr.vbs /ato
</code></pre>
<p>The codes, each from Microsoft's error-code table:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Microsoft's text</th>
<th>Usually points at (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>0xC004F074</td>
<td>"No Key Management Service (KMS) could be contacted."</td>
<td>Every host it tried failed; Event 12288 says why (Step 3)</td>
</tr>
<tr>
<td>0x8007232B</td>
<td>"DNS name does not exist."</td>
<td>No <code>_vlmcs</code> SRV record in the zones the client searches (Step 4)</td>
</tr>
<tr>
<td>0x8007007B</td>
<td>"0x8007007b DNS name does not exist."</td>
<td>Microsoft: "This issue can occur if the KMS client can't find the KMS SRV resource records in DNS." (Step 4)</td>
</tr>
<tr>
<td>0x800706BA</td>
<td>"The RPC server is unavailable."</td>
<td>Microsoft's causes: the host's firewall and "DNS SRV records are stale." (Steps 4, 5)</td>
</tr>
<tr>
<td>0xC004F038</td>
<td>"The count reported by your Key Management Service (KMS) is insufficient."</td>
<td>Below 25 clients or 5 servers (Step 7)</td>
</tr>
<tr>
<td>0xC004F06C</td>
<td>"The Key Management Service (KMS) determined that the request timestamp is invalid."</td>
<td>Clock skew (Step 8)</td>
</tr>
<tr>
<td>0xC004F039</td>
<td>"The Key Management Service (KMS) is not enabled."</td>
<td>Microsoft: "This issue occurs when KMS doesn't respond to a KMS request." (Step 5)</td>
</tr>
<tr>
<td>0xC004F041</td>
<td>"The Software Protection Service determined that the Key Management Server (KMS) is not activated."</td>
<td>Microsoft: "This issue occurs when the KMS host hasn't been activated." (Step 6)</td>
</tr>
<tr>
<td>0xC004F042</td>
<td>"The Software Protection Service determined that the specified Key Management Service (KMS) cannot be used."</td>
<td>Wrong host for this product: "This scenario is common in mixed environments with application-specific and operating system-specific KMS hosts." (Steps 6, 9)</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Every machine fails since the old host was retired</td>
<td>Stale <code>_vlmcs</code> record, new host not published</td>
<td>Steps 3, 4</td>
</tr>
<tr>
<td>Only the newest OS fails (Server 2025, latest Windows 11)</td>
<td>Host OS or CSVLK too old, or missing the required update</td>
<td>Step 6</td>
</tr>
<tr>
<td>Only one VLAN, site, or the VPN fails</td>
<td>TCP 1688 blocked, or a different DNS zone</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>Clients fail with 0xC004F038, host count stuck low</td>
<td>Threshold not met, or duplicate CMIDs from the image</td>
<td>Step 7</td>
</tr>
<tr>
<td>One VM fails after a restore</td>
<td>Clock more than four hours off</td>
<td>Step 8</td>
</tr>
<tr>
<td>Windows activates, Office doesn't</td>
<td>Office VL pack missing on the host</td>
<td>Step 9</td>
</tr>
<tr>
<td>An Azure VM fails</td>
<td>Azure's KMS endpoint or a forced-tunnel route</td>
<td>Step 10</td>
</tr>
<tr>
<td>A home or personal PC shows 0xC004F074</td>
<td>Not a KMS problem in your network: a volume key on a device that never belonged to one (Step 10)</td>
<td>Step 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read what the client thinks it is (read-only)</h2>
<p>Microsoft's KMS guidelines start here: "To see verbose output from the Software Licensing service, open an elevated Command Prompt window, or PowerShell window, and run slmgr.vbs /dlv." <code>/dlv</code> will "Display detailed license information." and "This operation doesn't require elevated privileges."</p>
<pre><code class="language-console">:: Read-only (writes local files). Detailed and short license info, plus the expiration date
cscript %windir%\system32\slmgr.vbs /dlv &gt; C:\Temp\dlv-client.txt
cscript %windir%\system32\slmgr.vbs /dli
cscript %windir%\system32\slmgr.vbs /xpr
</code></pre>
<p><code>/xpr</code> will "Display the activation expiration date for the product." and Microsoft adds that it's "primarily useful for KMS clients, because MAK and retail activation is perpetual." What to read in <code>/dlv</code> (field names from Microsoft's guidelines):</p>
<table>
<thead>
<tr>
<th>Field</th>
<th>What it tells you (Microsoft's text unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Name</td>
<td>"The Name tells you which edition of Windows the system is using."</td>
</tr>
<tr>
<td>Description</td>
<td><code>VOLUME_KMSCLIENT</code> is a KMS client. "If you see a different value here, such as MAK, you must reinstall the GVLK to configure this system as a KMS client."</td>
</tr>
<tr>
<td>Partial Product Key</td>
<td>"The Partial Product Key determines whether the KMS Client Setup Key matches the OS the KMS client is using."</td>
</tr>
<tr>
<td>License Status</td>
<td>"For a system activated by KMS, this value should be Licensed." Grace means it hasn't reached a host yet</td>
</tr>
<tr>
<td>KMS Machine Name from DNS</td>
<td>"The KMS Machine Name from DNS shows both the FQDN of the KMS host that the client successfully used for activation and which TCP port it used to communicate."</td>
</tr>
<tr>
<td>KMS Host Caching</td>
<td>"If the client can't contact the cached KMS host, it queries DNS to discover a new KMS host."</td>
</tr>
<tr>
<td>Client Machine ID (CMID)</td>
<td>"The CMID should be unique in every KMS client that the KMS host is able to activate." (Step 7)</td>
</tr>
</tbody></table>
<p>The same data from WMI, which is easier to compare across machines (class and property names from Microsoft's SoftwareLicensingProduct and SoftwareLicensingService pages; the filter is ours):</p>
<pre><code class="language-powershell"># Read-only. The Windows license that has a key installed, then the client-side KMS settings
Get-CimInstance -ClassName SoftwareLicensingProduct -Filter "PartialProductKey IS NOT NULL" |
  Where-Object Name -like 'Windows*' |
  Select-Object Name, Description, LicenseStatus, GracePeriodRemaining, PartialProductKey,
    KeyManagementServiceMachine, KeyManagementServicePort,
    DiscoveredKeyManagementServiceMachineName, DiscoveredKeyManagementServiceMachinePort
Get-CimInstance -ClassName SoftwareLicensingService |
  Select-Object Version, ClientMachineID, KeyManagementServiceHostCaching
</code></pre>
<p><code>LicenseStatus</code> values, from Microsoft's SoftwareLicensingProduct page: 0 Unlicensed, 1 Licensed, 2 OOBGrace, 3 OOTGrace, 4 NonGenuineGrace, 5 Notification, 6 ExtendedGrace. <code>DiscoveredKeyManagementServiceMachineName</code> "Specifies the last discovered KMS host name through DNS." <code>KeyManagementServiceMachine</code> is the host set by hand; empty means auto-discovery (our reading of "Returns null if SetKeyManagementServiceMachine has not been called.").</p>
<p><strong>Where the client looks, in order.</strong> Microsoft's timing and discovery reference lists it: the SKU-specific <code>KeyManagementServiceName</code> registry value, the AppID-specific value, the global value, the "SKU-specific cached KMS host", then "DNS auto-discovery (weight and priority honored)". And: "If a cached KMS does not respond, discovery is performed." A forgotten <code>/skms</code> from years ago sits at the top of that list:</p>
<pre><code class="language-console">:: Read-only. A hand-set KMS host or port on this client (no value = auto-discovery; error if absent is expected)
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" /v KeyManagementServiceName
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" /v KeyManagementServicePort
</code></pre>
<p>Microsoft's registry reference on that value: "Set this value to force the use of a specific KMS host by the KMS client." The SKU- and AppID-specific copies live in subkeys under the same path (Microsoft's reference); <code>reg query</code> with <code>/s</code> reads them too (our form).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Description says MAK or RETAIL</td>
<td>Not a KMS client; KMS isn't involved</td>
<td>Step 10, or install the right GVLK (Step 11)</td>
</tr>
<tr>
<td>Name shows a Home edition</td>
<td>Microsoft's KMS client setup key tables list no Home edition, so KMS can't activate it (our reading)</td>
<td>Step 10</td>
</tr>
<tr>
<td><code>KeyManagementServiceName</code> set to an old host</td>
<td>Pinned to the retired server; DNS never consulted</td>
<td>Step 11: <code>/ckms</code></td>
</tr>
<tr>
<td><code>DiscoveredKeyManagementServiceMachineName</code> is the retired host</td>
<td>The last host it found; DNS may still hand it out</td>
<td>Step 4</td>
</tr>
<tr>
<td>LicenseStatus 1, ticket says not activated</td>
<td>Look at the right product: Office or another edition</td>
<td>Step 9</td>
</tr>
<tr>
<td>Same CMID on two machines</td>
<td>Image not generalized</td>
<td>Step 7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Read Events 12288 and 12289 on the client (read-only)</h2>
<p>Microsoft's fix for 0xC004F074 starts in this log: "Open the Application Event Log." and "Identify each event associated with the activation attempt that has an Event ID of 12288." Microsoft's known-issues page names the source: "Except for KMS events that have event ID 12290, Windows logs all activation events to the Application event log under the event provider name Microsoft-Windows-Security-SPP."</p>
<pre><code class="language-powershell"># Read-only. Activation requests (12288) and host responses (12289) on this client, newest first
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'Microsoft-Windows-Security-SPP'; Id = 12288, 12289 } -MaxEvents 20 |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>How to read them (Microsoft's text; the field order is our reading of Microsoft's sample event):</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>What it says</th>
<th>What to read</th>
</tr>
</thead>
<tbody><tr>
<td>12288</td>
<td>"The client has sent an activation request to the key management service machine."</td>
<td>The Info line: the first value is the result code, then the host FQDN and port it tried (Microsoft's sample shows <code>0xC004F06C, 0x00000000, &lt;KMS Host FQDN&gt;:1688, ...</code>)</td>
</tr>
<tr>
<td>12288 with no 12289</td>
<td>"If you see only event ID 12288 without a corresponding event ID 12289, either the KMS client couldn't reach the KMS host, the KMS host didn't respond, or the client didn't receive the host's response."</td>
<td>Steps 4-6: name, port, host</td>
</tr>
<tr>
<td>12288 and 12289</td>
<td>"A failed activation or reactivation might also have both events."</td>
<td>12289's result</td>
</tr>
<tr>
<td>12289</td>
<td>"Activation Flag, which indicates whether the activation succeeded (1) or failed (0)." It also shows the "Current Count on the KMS Host"</td>
<td>Step 7 if the count is low</td>
</tr>
</tbody></table>
<p>Microsoft's known-issues checklist for 12289: "Is the result code 0? Anything else is an error." Then "Is the KMS host name in the event correct?" and "Is the KMS port correct?" To turn a result code into words, Microsoft: "IT pros can run Slui.exe to display a description of most activation-related error codes."</p>
<pre><code class="language-console">:: Read-only. Describe a result code taken from the 12288 or 12289 Info line (code is an example)
slui.exe 0x2a 0xC004F06C
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>12288 names the retired host</td>
<td>DNS or the cache still hands it out</td>
<td>Step 4</td>
</tr>
<tr>
<td>12288 names the right host, no 12289</td>
<td>Port, firewall, or the host itself</td>
<td>Steps 5, 6</td>
</tr>
<tr>
<td>Result 0xC004F06C</td>
<td>Timestamp invalid: clock skew</td>
<td>Step 8</td>
</tr>
<tr>
<td>Result 0xC004F038 in 12289, low count shown</td>
<td>Threshold not met</td>
<td>Step 7</td>
</tr>
<tr>
<td>Result 0xC004F042</td>
<td>That host can't activate this product</td>
<td>Steps 6, 9</td>
</tr>
<tr>
<td>No 12288 at all after <code>/ato</code></td>
<td>The client found no host to ask: DNS (0x8007232B)</td>
<td>Step 4</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: What does DNS hand this client? (read-only)</h2>
<p>KMS clients find hosts through SRV records. Microsoft's guidelines: "The KMS clients use Domain Name System (DNS) SRV records ( _vlmcs._tcp) to identify the KMS host." And, from the DNS troubleshooting guide: "If the wizard queries DNS and does not find the DNS entry for the KMS host computer, the wizard reports an error." Microsoft's own read is <code>nslookup -type=all _vlmcs._tcp&gt;kms.txt</code>; asking for SRV only is the same lookup with less output (our form, <code>-type=</code> per Microsoft's nslookup page):</p>
<pre><code class="language-console">:: Read-only. SRV records the client's resolver returns for its own suffix, then for a named zone (zone is an example)
nslookup -type=srv _vlmcs._tcp
nslookup -type=srv _vlmcs._tcp.contoso.com
</code></pre>
<p>Microsoft's expected answer looks like <code>_vlmcs._tcp.contoso.com SRV service location:</code> followed by priority, weight, <code>port = 1688</code> and <code>svr hostname = kms-server.contoso.com</code>. Then check that each target is a live host on the current address:</p>
<pre><code class="language-powershell"># Read-only. Every KMS SRV record for the zone, then each target's address (zone is an example)
$srv = Resolve-DnsName -Name '_vlmcs._tcp.contoso.com' -Type SRV -DnsOnly | Where-Object Type -eq 'SRV'
$srv | Select-Object Name, NameTarget, Port, Priority, Weight
foreach ($r in $srv) { Resolve-DnsName -Name $r.NameTarget -Type A -DnsOnly | Select-Object Name, IPAddress }
</code></pre>
<p><strong>The suffix the client searches.</strong> Microsoft: "If the nslookup command finds the KMS host, it does not mean that the DNS client can find the KMS host." Its next check: "Verify that the search list of the primary DNS suffix contains the DNS domain suffix that is associated with the KMS host." The zone a client searches is its primary DNS suffix or, for domain members, "the computer's domain as assigned by the DNS system (such as Active Directory Domain Services (AD DS) DNS)"; for workgroup machines it's the DHCP-assigned domain (Microsoft's guide).</p>
<pre><code class="language-console">:: Read-only. Primary DNS suffix, suffix search list, and DNS servers this client uses
ipconfig /all
</code></pre>
<pre><code class="language-powershell"># Read-only. The suffix search list as PowerShell sees it
Get-DnsClientGlobalSetting | Select-Object SuffixSearchList, UseSuffixSearchList
</code></pre>
<p><strong>On the DNS server, read the records themselves.</strong> A retired host's record stays until someone removes it. Microsoft's known-issues page on a failed host: "If the new host has a different computer name, you can manually remove the DNS SRV RR of the failed host or (if scavenging is enabled in DNS) let DNS automatically remove it." If scavenging is on and the record is still there, <a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> covers why.</p>
<pre><code class="language-powershell"># Read-only. On a DNS server: the _vlmcs SRV records in the zone, with their timestamps (zone is an example)
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -Name '_vlmcs._tcp' -RRType Srv |
  Select-Object HostName, Timestamp, TimeToLive, @{ n = 'Target'; e = { $_.RecordData.DomainName } }, @{ n = 'Port'; e = { $_.RecordData.Port } }
</code></pre>
<p>The <code>RecordData</code> field names are our reading (read them with <code>Format-List *</code> first). A <code>Timestamp</code> means the record was registered dynamically; an empty one means static (our reading, from DNS aging). Microsoft on registration: "By default, a KMS host server dynamically registers a DNS SRV record one time every 24 hours." A dynamic record that stopped refreshing a day after the host died is the retired-host fingerprint (our reading).</p>
<p><strong>More hosts than you think.</strong> Microsoft: "DNS returns the list of KMS hosts in a random order." and "If the session setup fails, the client randomly picks another KMS host." 0xC004F074 means all of them failed. Extra hosts also split the count: Microsoft warns "if there are too many KMS hosts available to the clients (either legitimate or unsupported systems), then the count might be distributed over all of them."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No SRV record (0x8007232B)</td>
<td>The host never published, or publishes to another zone</td>
<td>Step 6: DNS publishing</td>
</tr>
<tr>
<td>SRV record targets the retired host</td>
<td>Stale record (Microsoft's "incorrect or old" cause)</td>
<td>Step 11: remove it</td>
</tr>
<tr>
<td>SRV record right, A record old</td>
<td>Host moved; its A record didn't</td>
<td>Step 11: fix the A record</td>
</tr>
<tr>
<td>Record in <code>corp.contoso.com</code>, clients search <code>contoso.com</code></td>
<td>Different zones</td>
<td>Step 11: multi-domain publishing or <code>/skms-domain</code></td>
</tr>
<tr>
<td>An unknown host in the list</td>
<td>Someone's lab host or an unsupported system</td>
<td>Find its owner before deleting anything</td>
</tr>
<tr>
<td><code>nslookup</code> finds it, <code>/ato</code> still says DNS</td>
<td>Suffix search list</td>
<td>Step 11: suffix list or <code>/skms-domain</code></td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Can the client reach TCP 1688 on that host? (read-only)</h2>
<p>Microsoft's 0xC004F074 article gives the test: "use the Test-NetConnection PowerShell cmdlet to check if the port is open between the client and server." Use the host name from Event 12288 or the SRV target:</p>
<pre><code class="language-powershell"># Read-only. From the failing client: TCP 1688 on the KMS host (host name is an example)
Test-NetConnection -ComputerName kms01.contoso.com -Port 1688 |
  Select-Object ComputerName, RemoteAddress, RemotePort, TcpTestSucceeded
</code></pre>
<p>Microsoft: "Review the TcpTestSucceeded output parameter. If it's False, it means that port 1688 is blocked between the KMS client and the KMS server." And from the DNS guide: "The firewalls on both the KMS client and the KMS host must allow communication over port 1688." Microsoft's known-issues page adds the network in between: "Make sure that routers do not block traffic using TCP port 1688 (if the default port is used) and that stateful traffic to the KMS client is allowed."</p>
<p><strong>On the host, read the listener and its firewall rule.</strong> Microsoft: "By default, the KMS host has a firewall exception for KMS, but this exception isn't automatically enabled." Microsoft's host setup page uses the rule name <code>SPPSVC-In-TCP</code>:</p>
<pre><code class="language-powershell"># Read-only. On the KMS host: is anything listening on 1688, and is the KMS firewall rule enabled and for which profiles?
Get-NetTCPConnection -LocalPort 1688 -State Listen -ErrorAction SilentlyContinue |
  Select-Object LocalAddress, LocalPort, OwningProcess
Get-NetFirewallRule -Name SPPSVC-In-TCP | Select-Object Name, DisplayName, Enabled, Profile, Direction
Get-NetFirewallRule -Name SPPSVC-In-TCP | Get-NetFirewallPortFilter | Select-Object Protocol, LocalPort
Get-NetConnectionProfile | Select-Object InterfaceAlias, NetworkCategory
</code></pre>
<p>A rule enabled only for Domain while the host's NIC is on Public blocks the clients (our reading; <code>Get-NetConnectionProfile</code> shows the category). Microsoft's guidelines also say "Communication with KMS uses anonymous RPC." and include an inbound TCP 135 rule next to the 1688 one in their firewall example; treat 135 as Microsoft's example, not a documented requirement for every client (our reading). Microsoft's VAMT KMS page: "Clients require RPC over TCP/IP connectivity to the KMS host to successfully activate."</p>
<p><strong>A port other than 1688.</strong> <code>/sprt</code> "sets the port on which the KMS host listens for client activation requests. The default TCP port is 1688." Microsoft: "If you change the default port number in the KMS host configuration, also adjust the port number that you use for the resource records."</p>
<p><strong>Name vs SRV, without changing anything permanent yet.</strong> Microsoft's DNS guide isolates the two by pointing one client at the host by name, then by IP, and running <code>/ato</code> each time: "If these commands resolve the problem, this is an SRV record issue." and, for the IP form, "If these commands resolve the problem, this is most likely a name resolution issue." That sets a registry value on the client, so it's in Step 11 with its undo (<code>/ckms</code>).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>TcpTestSucceeded</code> False from one VLAN or the VPN</td>
<td>Network ACL or firewall between them</td>
<td>Network change for that one port</td>
</tr>
<tr>
<td>False from everywhere, host listening</td>
<td>Host firewall rule disabled or wrong profile</td>
<td>Step 11: enable the rule</td>
</tr>
<tr>
<td>Nothing listening on 1688</td>
<td>sppsvc stopped, KMS not enabled, or a different port</td>
<td>Step 6</td>
</tr>
<tr>
<td>True, <code>/ato</code> still 0xC004F074</td>
<td>The host answers but refuses: licensing, version, count, time</td>
<td>Steps 6-8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Is the host licensed, publishing, and new enough? (read-only)</h2>
<p>Run the same <code>/dlv</code> on the host. Microsoft's guidelines: "The License Status shows the status of the KMS host system. The value should be Licensed. Any other value means you should reactivate the host." Microsoft's Office host page shows the KMS section of that output: <code>Key Management Service is enabled on this machine</code>, <code>Current count</code>, <code>Listening on Port: 1688</code>, <code>DNS publishing enabled</code>, <code>KMS priority: Normal</code>.</p>
<pre><code class="language-console">:: Read-only (writes one local file). On the KMS host: detailed license info for every installed product (Windows and any Office host keys)
cscript %windir%\system32\slmgr.vbs /dlv all &gt; C:\Temp\dlv-host.txt
</code></pre>
<pre><code class="language-powershell"># Read-only. On the KMS host: the Software Protection service, KMS settings from WMI, and the KMS registry values
Get-Service -Name sppsvc | Select-Object Name, Status, StartType
Get-CimInstance -ClassName SoftwareLicensingService |
  Select-Object Version, KeyManagementServiceListeningPort, KeyManagementServiceDnsPublishing, KeyManagementServiceLowPriority
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform' |
  Select-Object DisableDnsPublishing, DnsDomainPublishList, KeyManagementServiceListeningPort, EnableKmsLowPriority
</code></pre>
<p>Microsoft's first 0xC004F074 cause is the service: "Check if the sppsvc service is running on the KMS server. If the service is stopped, start it." The registry values, from Microsoft's registry reference: <code>DisableDnsPublishing</code> "Set this to a non-zero value to block auto-publishing to DNS by the KMS host."; <code>DnsDomainPublishList</code> lists extra zones and "The KMS home domain is always used, so it is not necessary to include it here."; <code>EnableKmsLowPriority</code> carries a warning: "Note that on a busy system, this can prevent the KMS host from maintaining a sufficient count."</p>
<p><strong>The host's own log.</strong> Microsoft: "If you're troubleshooting a client and can't find a corresponding event ID 12290 on the KMS host, then the client isn't connecting to the KMS host." The reasons it lists include "The firewall is blocking TCP 1688." and "The event log is full." And Event 12293: "This event indicates that the host didn't publish the required records in DNS."</p>
<pre><code class="language-powershell"># Read-only. On the KMS host: recent activation requests (12290) and DNS publishing failures (12293)
Get-WinEvent -FilterHashtable @{ LogName = 'Key Management Service'; Id = 12290, 12293 } -MaxEvents 50 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
Get-WinEvent -ListLog 'Key Management Service' | Select-Object LogName, RecordCount, MaximumSizeInBytes, LogMode
</code></pre>
<p>For a 12290 from the failing client, Microsoft lists what to read: "The Minimum count needed to activate", "The CMID, which is a unique value on each system.", and "The License State and Time to State Expiration". For 12293, Microsoft's known-issues page: "Domain Name System (DNS) may restrict Write access or may not support dynamic DNS (DDNS)." It also says a second host may not be able to update the existing SRV record unless the SRV default permissions are changed.</p>
<p><strong>Can this host activate this client?</strong> The CSVLK installed on the host decides which editions it can activate, and the host's OS decides which CSVLKs it can hold (Microsoft's activation versions table). Microsoft: "Updates are required when you want to activate a version of Windows that is newer than the version your KMS host is running." From Microsoft's required-updates table:</p>
<table>
<thead>
<tr>
<th>KMS host OS</th>
<th>To activate</th>
<th>Required update (Microsoft's table)</th>
</tr>
</thead>
<tbody><tr>
<td>Windows Server 2022</td>
<td>Windows Server 2025</td>
<td>February 13, 2024 - KB5034765 or later cumulative update</td>
</tr>
<tr>
<td>Windows Server 2019</td>
<td>Windows Server 2025</td>
<td>February 13, 2024 - KB5034768 or later cumulative update</td>
</tr>
<tr>
<td>Windows Server 2019</td>
<td>Windows Server 2022</td>
<td>June 8, 2021 - KB5003646 or later cumulative update</td>
</tr>
<tr>
<td>Windows Server 2016</td>
<td>Windows Server 2022, Windows Server 2019</td>
<td>June 8, 2021 - KB5003638 or later cumulative update</td>
</tr>
</tbody></table>
<p>The update is half of it. The host also needs a CSVLK from a group that covers the client: Microsoft's table lists "Volume License for Windows Server 2025" as hostable on Windows Server 2025, 2022 and 2019, and it activates Windows Server 2025 down to 2008 plus current Windows clients. Microsoft's planning page also limits client-OS hosts: "a KMS host running on a Windows client operating system can only activate computers also running client operating systems". The <code>/dlv</code> Description and Name lines show which CSVLK the host carries (Microsoft's guidelines: "The Description shows you which key is currently installed.").</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>License Status not Licensed</td>
<td>Host never activated (0xC004F041 on clients)</td>
<td>Step 11: activate the host</td>
</tr>
<tr>
<td>sppsvc stopped</td>
<td>Microsoft's first cause</td>
<td>Step 11: start it</td>
</tr>
<tr>
<td>No "Key Management Service is enabled" section</td>
<td>No CSVLK installed; it's just another client</td>
<td>Step 11: install and activate the CSVLK</td>
</tr>
<tr>
<td>No 12290 for the failing client</td>
<td>Request never arrived: DNS, port, or the log is full</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>12293 present</td>
<td>Publishing failed: DNS permissions or no dynamic updates</td>
<td>Step 11: DNS permissions or manual record</td>
</tr>
<tr>
<td><code>DisableDnsPublishing</code> = 1</td>
<td>Publishing off by design; someone owns a manual record</td>
<td>Step 4: is that record current?</td>
</tr>
<tr>
<td>Host is Server 2019 with a 2019 CSVLK, client is Server 2025</td>
<td>CSVLK group too old</td>
<td>Step 11: a newer CSVLK on a supported host</td>
</tr>
<tr>
<td>Host is missing the update in the table</td>
<td>Can't activate the newer OS</td>
<td>Step 11: patch the host</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: Is the count high enough? (read-only)</h2>
<p>A host that answers can still refuse to activate. Microsoft's planning page: "Clients activate if the count is 25 or higher. Servers and volume editions of Microsoft Office products activate if the count is five or greater." And: "The KMS only counts unique connections from the past 30 days, and only stores the 50 most recent contacts." Below the threshold, clients get 0xC004F038, and Microsoft's fix is "Add computers to the KMS pool." Microsoft's guidelines on the counter: "If the count is 0, either the service was recently activated or no valid clients are connected to the KMS host."</p>
<pre><code class="language-powershell"># Read-only. On the KMS host: current count and the minimum this host requires (Windows host license only; filter is ours)
Get-CimInstance -ClassName SoftwareLicensingProduct -Filter "PartialProductKey IS NOT NULL" |
  Where-Object { $_.Name -like 'Windows*' -and $_.IsKeyManagementServiceMachine } |
  Select-Object Name, KeyManagementServiceCurrentCount, RequiredClientCount, KeyManagementServiceTotalRequests, KeyManagementServiceFailedRequests
</code></pre>
<p><code>KeyManagementServiceCurrentCount</code>: "A value of -1 indicates the host is not enabled as a KMS or that it has not received any client-licensing requests." <code>RequiredClientCount</code> "Specifies the minimum number of clients that are required to connect to a KMS host to enable volume licensing." The <code>/dlv</code> cumulative-request section is usually noise; Microsoft: "Generally, this data isn't helpful for troubleshooting." The exception it names: if the count isn't rising, "you should see the number of failed requests increasing."</p>
<p><strong>Count stuck at 1.</strong> Microsoft's guidelines: "A common issue related to the count happens when the environment has an activated KMS host and enough clients, but the count doesn't increase beyond one." The cause is the image: "If this value isn't unique, it's because the image wasn't correctly configured for distribution using sysprep." Compare CMIDs across two machines from the same image:</p>
<pre><code class="language-powershell"># Read-only. CMID on several clients from the same image (computer names are examples; needs WinRM/WMI access)
'PC001', 'PC002', 'PC003' | ForEach-Object {
  Get-CimInstance -ComputerName $_ -ClassName SoftwareLicensingService | Select-Object PSComputerName, ClientMachineID
}
</code></pre>
<p>Microsoft's other count cause: "Another reason why the count might not be increasing is that there are too many KMS hosts in the environment and the count is distributed over all of them." Step 4's SRV list shows how many hosts the clients see.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Count under 25, Windows clients failing</td>
<td>Not enough unique clients in 30 days</td>
<td>Step 11: wait for the pool, or use AD-based activation or MAK for small sites</td>
</tr>
<tr>
<td>Count under 5, servers failing</td>
<td>Same, for servers</td>
<td>Step 11</td>
</tr>
<tr>
<td>Count stuck at 1 or 2</td>
<td>Duplicate CMIDs</td>
<td>Step 11: fix the image</td>
</tr>
<tr>
<td>Two hosts each with half the count</td>
<td>Count split across hosts</td>
<td>Step 11: retire the extra host and its record</td>
</tr>
<tr>
<td>Count -1</td>
<td>KMS not enabled, or no requests yet</td>
<td>Step 6</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: Are the clocks within four hours? (read-only)</h2>
<p>Microsoft's 0xC004F074 article ties the time case to the code in the 12288 Info line: "The error 0xC004F06C listed in the info section may occur if the difference between system time on the client computer and the system time on the KMS host is more than 4 hours." And: "The time zone set on the client computer doesn't affect the activation since it's based on UTC." A VM restored from an old checkpoint, or a host with a dead CMOS battery, is the usual setup (our reading).</p>
<pre><code class="language-console">:: Read-only. This machine's time source and state, then its offset from the KMS host (host name is an example)
w32tm /query /status
w32tm /stripchart /computer:kms01.contoso.com /dataonly /samples:3
</code></pre>
<p><code>/stripchart</code> with <code>/dataonly</code> "Displays the data only, without graphics." Microsoft's fix: "We recommend that you use a Network Time Protocol (NTP) time source or the Active Directory service to synchronize the time between computers." If a domain member's time is wrong, <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> is the read-first path for the cause; resync only after you know why it drifted (our habit).</p>
<hr />
<h2>Step 9: Office volume activation (read-only)</h2>
<p>Volume Office uses the same KMS host but its own client tool. Microsoft: "Any changes you make affect only Office client products. Configure the Windows client separately by using the slmgr.vbs script." The <code>ospp.vbs</code> script is in <code>Program Files\Microsoft Office\root\Office16</code> (or <code>Program Files (x86)</code> for 32-bit Office on 64-bit Windows). Microsoft also notes "The ospp.vbs script doesn't work for Microsoft 365 Apps or subscription versions of Project and Visio." Those aren't KMS-activated, so they're out of this post's scope.</p>
<pre><code class="language-console">:: Read-only. On the Office client (64-bit Office path shown): license status, KMS activation history, CMID, and an error code's description
cd /d "%ProgramFiles%\Microsoft Office\root\Office16"
cscript ospp.vbs /dstatus
cscript ospp.vbs /dhistorykms
cscript ospp.vbs /dcmid
cscript ospp.vbs /ddescr:0xC004F074
</code></pre>
<p><code>/dstatus</code> "Displays license information for installed product keys." and <code>/dhistorykms</code> "Displays KMS client activation history." On the host, Office needs its own pack and key: "To enable the KMS host computer to activate Office, you must install the Office Volume License Pack and enter your KMS key on the KMS host computer." Microsoft's Office host page also lists the minimum host OS per Office version (for example, Office LTSC 2024 needs "At least Windows Server 2019 or at least Windows 10 (volume editions)"). Step 6's <code>/dlv all</code> output on the host shows an Office KMS host entry when the pack and key are in place (Microsoft's sample: <code>Name: Office 16, OfficeKMSHostVL_KMS_Host edition</code>).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No Office entry in the host's <code>/dlv all</code></td>
<td>VL pack or Office CSVLK not installed</td>
<td>Step 11</td>
</tr>
<tr>
<td>Office 0xC004F042</td>
<td>The host Office reached can't activate this Office version</td>
<td>Step 11: the right pack on the right host</td>
</tr>
<tr>
<td><code>/dhistorykms</code> shows an old host name</td>
<td>Office pinned with <code>/sethst</code></td>
<td>Step 11: <code>/remhst</code></td>
</tr>
<tr>
<td>Product is Microsoft 365 Apps</td>
<td>Not KMS</td>
<td>Microsoft 365 licensing, not this post</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Other activation paths (read-only)</h2>
<p><strong>Active Directory-based activation.</strong> If the forest has an activation object, domain members with a GVLK activate from AD, not KMS. Microsoft: "If the ADDS object can't be retrieved, client computers use KMS activation." and "The slmgr.vbs /dlv command also indicates if KMS was used." <code>slmgr /ao-list</code> "Displays all of the activation objects that are available to the local computer." Microsoft calls it "the best option for activating client computers and servers" where every machine is domain joined and supported, but a KMS host is still needed for "Workgroup computers outside the domain".</p>
<pre><code class="language-console">:: Read-only. AD activation objects this machine can see
cscript %windir%\system32\slmgr.vbs /ao-list
</code></pre>
<p><strong>An activation type set by policy.</strong> <code>/act-type</code> "sets a value in the registry that limits volume activation to a single type. Activation type 1 limits activation to Active Directory only." A machine limited to type 1 never tries KMS (our reading). The value is <code>VLActivationType</code> under the product's AppID and SKU subkeys (Microsoft's registry reference); <code>reg query</code> with <code>/s /f VLActivationType</code> on the SoftwareProtectionPlatform key finds it (our form).</p>
<p><strong>Disjoint namespaces.</strong> <code>/skms-domain</code> exists for exactly this: "Use this option, especially in disjoint namespace environments, to force KMS to ignore the DNS suffix search list and look for KMS host records in the specified DNS domain instead."</p>
<p><strong>Azure VMs.</strong> Azure VMs activate against Azure's KMS (<code>azkms.core.windows.net:1688</code> in the public cloud, per Microsoft's table), not yours. Microsoft's forced-tunneling article: "The activation requires that the activation request come from an Azure public IP address." Test that endpoint with Step 5's <code>Test-NetConnection</code>, from the VM.</p>
<p><strong>A device that isn't on the work network, or isn't a work device.</strong> Microsoft's consumer help lists 0xC004F074 with two causes: "You might see this error if you entered a product key for the Enterprise edition of Windows to activate Windows 11 Home or Windows 11 Pro." and "you're trying to activate your work device but aren't connected to your workplace's network". For an off-network work laptop, connect it (or the VPN) and rerun Steps 4-5. For a personal device, the fix is a key for that edition; Microsoft's KMS guide says "If this is incorrect, obtain a Retail key from the Microsoft Store." Third-party KMS activator tools and public KMS servers aren't a fix: they activate against a host you don't control and outside your licence (our view).</p>
<hr />
<h2>Common false starts</h2>
<table>
<thead>
<tr>
<th>What people try</th>
<th>Why it misleads (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td><code>slmgr /skms</code> on every machine</td>
<td><code>/skms</code> "disables auto-detection of the KMS host." Every pinned machine breaks again when that host moves (Microsoft's failed-host note)</td>
</tr>
<tr>
<td><code>slmgr /rearm</code> across the fleet</td>
<td>"This option resets the activation timers." It doesn't find a host; rearms are limited</td>
</tr>
<tr>
<td>Installing the KMS host key on clients</td>
<td>Microsoft: "KMS keys should be installed only on KMS hosts, not on KMS clients."</td>
</tr>
<tr>
<td>Switching everything to MAK</td>
<td>Uses up MAK activations to work around a DNS record</td>
</tr>
<tr>
<td>Rebuilding the KMS host first</td>
<td>A new host with the old record still in DNS fails the same way</td>
</tr>
<tr>
<td>Opening 1688 to the internet</td>
<td>Microsoft's planning page: "The KMS host must reside on your local network."</td>
</tr>
<tr>
<td>Deleting every <code>_vlmcs</code> record</td>
<td>Removes the working host along with the stale one</td>
</tr>
<tr>
<td>A public KMS server or activator script</td>
<td>Activates against a host you don't control (our view)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 11: Change one link, then prove it</h2>
<p>Save what you'll change first:</p>
<pre><code class="language-console">:: Read-only (writes local files). On the client and on the KMS host: license output and the SoftwareProtectionPlatform key
cscript %windir%\system32\slmgr.vbs /dlv all &gt; C:\Temp\dlv-before.txt
reg export "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SoftwareProtectionPlatform" C:\Temp\spp-before.reg /y
</code></pre>
<pre><code class="language-powershell"># Read-only (writes a local file). On a DNS server: the current _vlmcs records (zone is an example)
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -Name '_vlmcs._tcp' -RRType Srv | Format-List * &gt; C:\Temp\vlmcs-before.txt
</code></pre>
<p><strong>Host service stopped (Step 6).</strong> Microsoft's step: "If the service is stopped, start it."</p>
<pre><code class="language-powershell"># Change, on the KMS host. Start the Software Protection service (dry run first)
Start-Service -Name sppsvc -WhatIf
</code></pre>
<p><strong>Host firewall rule disabled (Step 5).</strong> Microsoft's host setup form, scoped to the profiles your clients use, dry-run first:</p>
<pre><code class="language-powershell"># Change, on the KMS host. Enable the built-in KMS rule for Domain and Private (Microsoft's form plus -WhatIf)
Set-NetFirewallRule -Name SPPSVC-In-TCP -Profile Domain,Private -Enabled True -WhatIf
</code></pre>
<p><strong>Host too old for the client (Step 6).</strong> Install the update from Microsoft's table, or move the CSVLK to a supported host OS with a CSVLK group that covers the client. Then rerun Step 6's <code>/dlv</code> on the host.</p>
<p><strong>Host not activated (Step 6).</strong> Microsoft's VAMT KMS page activates the host key with <code>cscript.exe slmgr.vbs /ato</code> on the host (or by phone with <code>/dti</code> and <code>/atp</code>). That's the one place <code>/ato</code> is a host step (our reading).</p>
<p><strong>Stale SRV record (Step 4).</strong> Remove only the record for the retired host. Microsoft's warning on <code>Remove-DnsServerResourceRecord</code>: "If you do not specify RecordData, the cmdlet deletes all records that match RRtype and Name for the specified zone." For SRV, Microsoft's example passes priority, weight, port, and target in that order:</p>
<pre><code class="language-powershell"># Change, on a DNS server. Remove the one SRV record that targets the retired host (zone and target are examples; dry run)
Remove-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType Srv -Name '_vlmcs._tcp' -RecordData '0', '0', '1688', 'kms-old.contoso.com.' -WhatIf
</code></pre>
<p>Microsoft's known-issues page also says to "delete any unnecessary SRV RRs from DNS, and then restart the computers." Clients that cached the old host fall back to discovery when it doesn't answer, per the timing reference; restarting the test client speeds that up (our reading).</p>
<p><strong>No SRV record, DNS without dynamic updates (Steps 4, 6).</strong> Microsoft's manual record is <code>_vlmcs</code>, <code>_tcp</code>, priority 0, weight 0, port 1688, the host's FQDN. Microsoft's PowerShell example for an SRV record uses <code>-Srv -Name -ZoneName -DomainName -Priority -Weight -Port</code>; with KMS values (our form):</p>
<pre><code class="language-powershell"># Change, on a DNS server. Create the KMS SRV record by hand (zone and host are examples; dry run)
Add-DnsServerResourceRecord -Srv -Name '_vlmcs._tcp' -ZoneName 'contoso.com' -DomainName 'kms01.contoso.com' -Priority 0 -Weight 0 -Port 1688 -WhatIf
</code></pre>
<p>If you create records by hand, Microsoft says to turn off publishing on the hosts: "By disabling publishing, you prevent event logs from collecting failed DNS publishing events." <code>/cdns</code> will "Disable DNS publishing by the KMS host."</p>
<p><strong>Clients in another zone (Step 4).</strong> Either publish there or point the clients there. Microsoft: "If the KMS host and KMS clients use different DNS zones, you must configure the KMS host to automatically publish its SRV records in multiple DNS domains." That's <code>DnsDomainPublishList</code> (REG_MULTI_SZ) on the host, then a restart of the Software Protection service: Microsoft: "This operation creates the SRV records." Or, per client or by policy, <code>/skms-domain</code>:</p>
<pre><code class="language-console">:: Change, on a client. Look for KMS SRV records in one named zone instead of the suffix list (zone is an example)
cscript %windir%\system32\slmgr.vbs /skms-domain contoso.com
</code></pre>
<p><strong>Isolate name vs SRV, or bridge while DNS is fixed (Steps 4, 5).</strong> Microsoft's test, on one client, followed by its undo:</p>
<pre><code class="language-console">:: Change, on one client (temporary). Point at the host by name, try activation; then restore auto-discovery
cscript %windir%\system32\slmgr.vbs /skms kms01.contoso.com:1688
cscript %windir%\system32\slmgr.vbs /ato
cscript %windir%\system32\slmgr.vbs /ckms
</code></pre>
<p><code>/ckms</code> "removes the specified KMS host name, address, and port information from the registry and restores KMS autodiscovery behavior." Microsoft on discovery: "We highly recommend that you use the automatic discovery process." If <code>/skms</code> by name fails, repeat with the host's IPv4 address to split name resolution from the SRV record (Microsoft's sequence).</p>
<p><strong>Count too low (Step 7).</strong> Wait for more unique clients inside 30 days, or use AD-based activation for domain members. For a small site that will never reach 25 Windows clients, MAK is Microsoft's alternative: "If you cannot install a KMS host or if you cannot use KMS activation, try the Change the product key to an MAK procedure."</p>
<p><strong>Duplicate CMIDs (Step 7).</strong> Fix the image, then the machines already deployed from it. Microsoft: "To reset the client computer ID (CMID) and other product-activation information, run sysprep /generalize or slmgr /rearm." Generalize the reference image before capture, not production machines (our habit). Microsoft's Sysprep page: "Generalizing the image removes computer-specific information such as installed drivers and the computer security identifier (SID)." For machines already deployed:</p>
<pre><code class="language-console">:: Change, on one deployed client with a duplicate CMID. Reset activation info, restart, then try KMS again
cscript %windir%\system32\slmgr.vbs /rearm
:: restart the machine, then:
cscript %windir%\system32\slmgr.vbs /ato
</code></pre>
<p><strong>Clock skew (Step 8).</strong> Fix the time source first; then Microsoft's command on the client: "Run the w32tm /resync command to resync the time on the client."</p>
<p><strong>Wrong key type (Step 2).</strong> For a machine that should be a KMS client, install the GVLK for its edition from Microsoft's KMS client setup keys page with <code>slmgr /ipk</code>, then <code>/ato</code>. Microsoft on <code>/ipk</code>: "If a key is already installed, it's silently replaced." Note the current key type in the ticket before you replace it (our habit).</p>
<p><strong>Office (Step 9).</strong> On the host, the Office Volume License Pack and the Office KMS key. On a client pinned to an old host, Microsoft's <code>/remhst</code> "Removes the KMS host name and resets the port to the default (1688)." Then <code>cscript ospp.vbs /act</code> and <code>/dhistorykms</code>.</p>
<p><strong>Then prove it.</strong> On the client, one activation and the two events; on the host, the matching 12290 and a count that moved:</p>
<pre><code class="language-console">:: Low-risk. On the client: one activation, then the license state (expect License Status: Licensed)
cscript %windir%\system32\slmgr.vbs /ato
cscript %windir%\system32\slmgr.vbs /dli
</code></pre>
<pre><code class="language-powershell"># Read-only. On the client: the newest 12288/12289 pair. On the host: the newest 12290 entries
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'Microsoft-Windows-Security-SPP'; Id = 12288, 12289 } -MaxEvents 2 |
  Select-Object TimeCreated, Id, Message | Format-List
Get-WinEvent -FilterHashtable @{ LogName = 'Key Management Service'; Id = 12290 } -MaxEvents 5 |
  Select-Object TimeCreated, Message | Format-List
</code></pre>
<p>The 12289 should show Activation Flag 1 (Microsoft: "whether the activation succeeded (1) or failed (0)"), and <code>/dlv</code> should show the expected host under KMS Machine Name from DNS. Then the next machine from the same VLAN, image, or site, because one machine passing doesn't clear the others (our habit).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>SRV record for a retired host</td>
<td>Remove that one record (<code>-RecordData</code>, <code>-WhatIf</code>)</td>
<td>Deleting every <code>_vlmcs</code> record</td>
</tr>
<tr>
<td>No record, DNS has no dynamic updates</td>
<td>A manual SRV record and <code>/cdns</code> on hosts</td>
<td><code>/skms</code> on every machine</td>
</tr>
<tr>
<td>Clients in another zone</td>
<td><code>DnsDomainPublishList</code> or <code>/skms-domain</code></td>
<td>Hand-pinning each client</td>
</tr>
<tr>
<td>TCP 1688 blocked</td>
<td>One rule for 1688 from the client networks</td>
<td>Opening the host to every network</td>
</tr>
<tr>
<td>Host too old for the client</td>
<td>The update from Microsoft's table, or a newer host and CSVLK</td>
<td>Converting the new servers to MAK</td>
</tr>
<tr>
<td>Count stuck at 1</td>
<td>Fix the image, then reset the deployed machines</td>
<td>Adding more hosts</td>
</tr>
<tr>
<td>Clock off by hours</td>
<td>Fix the time source, then resync</td>
<td>Rearming</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does error 0xC004F074 mean?</strong>
"No Key Management Service (KMS) could be contacted." The client tried every KMS host it found or was pointed at, and none activated it. Microsoft's causes: the host's Software Protection service stopped, a network block on TCP 1688, an incorrect or old SRV record in DNS, a clock difference of more than four hours, or a host that doesn't support the client's version. Event 12288 in the client's Application log names the host it tried and the result.</p>
<p><strong>How do I find the KMS server from a client?</strong>
Run <code>slmgr /dlv</code> and read KMS Machine Name from DNS, or query DNS with <code>nslookup -type=srv _vlmcs._tcp</code>. <code>Resolve-DnsName -Name _vlmcs._tcp.&lt;your domain&gt; -Type SRV</code> shows every published host with its port.</p>
<p><strong>Does KMS use TCP or UDP on port 1688?</strong>
TCP. Microsoft: "Any activation communications that occur between KMS clients and the KMS host use the 1688 TCP port." Test it from the client with <code>Test-NetConnection -ComputerName &lt;host&gt; -Port 1688</code>.</p>
<p><strong>How many clients does KMS need before it activates?</strong>
25 for Windows client editions, 5 for Windows Server and volume Office, counted as unique machines over the past 30 days. Below that, clients get 0xC004F038. A count stuck at 1 usually means duplicate CMIDs from an image that wasn't generalized.</p>
<p><strong>Should I use slmgr /skms to fix 0xC004F074?</strong>
As a test on one machine, yes: it splits SRV problems from name and port problems, and <code>/ckms</code> undoes it. As the fix, no: it turns off auto-discovery, so every pinned machine breaks again when that host changes. Fix the SRV record or the publishing zone instead.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-11c6">DNS Scavenging Not Working</a> (why a retired KMS host's SRV record can outlive the host)</li>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (the same DNS record and port reads, between domain controllers)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (the clock behind 0xC004F06C and the four-hour rule)</li>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (DNS and the DC locator, when a domain member can't find anything at all)</li>
<li><a href="https://dev.to/adminpackstudio/windows-server-dhcp-not-assigning-ip-addresses-a-read-first-scope-authorization-relay-and-d13">Windows Server DHCP Not Assigning IP Addresses</a> (DHCP hands workgroup clients the domain they search for SRV records)</li>
</ul>
<h2>Free checklist</h2>
<p>Pointing every machine at one KMS host with <code>slmgr /skms</code> before one client was tested against the fixed SRV record is a change nobody tested where the activations happen. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=kms-activation-error-0xc004f074">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=kms-activation-error-0xc004f074</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=kms-activation-error-0xc004f074">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=kms-activation-error-0xc004f074</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-10 favor read-only checks: <code>slmgr /dlv</code>, <code>/dli</code>, <code>/xpr</code>, <code>/ao-list</code>, <code>ospp.vbs /dstatus</code>, <code>/dhistorykms</code>, <code>/dcmid</code>, <code>/ddescr</code>, <code>slui 0x2a</code>, <code>Get-CimInstance</code>, <code>Get-WinEvent</code>, <code>reg query</code>, <code>nslookup</code>, <code>Resolve-DnsName</code>, <code>ipconfig /all</code>, <code>Get-DnsClientGlobalSetting</code>, <code>Get-DnsServerResourceRecord</code>, <code>Test-NetConnection</code>, <code>Get-NetTCPConnection</code>, <code>Get-NetFirewallRule</code>, <code>Get-NetFirewallPortFilter</code>, <code>Get-NetConnectionProfile</code>, <code>Get-Service</code>, <code>Get-ItemProperty</code>, and <code>w32tm /query</code> and <code>/stripchart</code>. <code>slmgr /ato</code> makes one activation attempt; output redirection and <code>reg export</code> write local files. Starting sppsvc, firewall rules, host updates and keys, SRV record changes, <code>/cdns</code>, <code>DnsDomainPublishList</code>, <code>/skms</code>, <code>/skms-domain</code>, <code>/ckms</code>, <code>/rearm</code>, <code>/ipk</code>, <code>w32tm /resync</code>, Office packs and <code>ospp.vbs /remhst</code> are deliberate changes. Change one link at a time, test from the failing network, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Revocation Server Was Offline (0x80092013): A Read-First CRL, CDP, Cache, and Offline Root Triage]]></title><description><![CDATA[Revocation server was offline or CRL expired? Read the chain with -urlfetch, each CRL's NextUpdate, the CDP as SYSTEM, and caches first.]]></description><link>https://adminpackstudio.hashnode.dev/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/revocation-server-was-offline-0x80092013-a-read-first-crl-cdp-cache-and-offline-root-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 18:43:23 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since about 7:30 this morning the 802.1X Wi-Fi rejects every laptop, the VPN says the certificate couldn't be validated, and the issuing CA service won't start. Its error says the revocation function was unable to check revocation because the revocation server was offline, 0x80092013. The PKI admin is out; the offline root has been powered off in a closet since spring. A forum post says to set CRLF_REVCHECK_IGNORE_OFFLINE on the CA, set NoRevocationCheck on both NPS servers, and turn off revocation checking in the VPN profile. Can we start with that?"</p>
<p>Not yet. <strong>"Revocation server offline" almost never means a server is down. It means a client building a certificate chain couldn't get a current CRL for one of the certificates in it: the CRL expired, the CDP URL can't be reached from that client's network or security context, the URL was never published, or the client is still using a stale cached copy.</strong> Microsoft's DirectAccess article for 0x80092013 lists those four causes word for word, starting with "The CRL location (CDP) is unreachable." and ending with "The CRL is reachable but the client is picking from an old cache." Its chain-validation note explains the blast radius: "Revocation checking is performed from the root CA certificate down to the evaluated certificate." One expired root CRL breaks every certificate under it, and Wi-Fi, VPN, and the CA itself fail together. Turning off revocation checking hides the failed link and leaves it failed, and a CRL that's still expired tomorrow breaks the next app nobody exempted. Work through it as a chain: <strong>which app, which error code, which certificate, which context, since when</strong> → <code>certutil -verify -urlfetch</code> <strong>on that certificate, from the failing machine</strong> → <strong>which CRL failed: issuing, root, or delta, and its NextUpdate</strong> → <strong>can this client, in this context, reach the CDP URL?</strong> → <strong>does the web or LDAP location serve the current file?</strong> → <strong>CAPI2 events, which name the exact action that failed</strong> → <strong>caches</strong> → <strong>the CA: publication events, schedule, and CDP settings</strong> → <strong>the offline root's CRL</strong> → <strong>what each relying party does with an unknown result</strong>. <code>certutil</code>, <code>Get-WinEvent</code>, <code>wevtutil</code>, <code>Invoke-WebRequest</code>, <code>Test-NetConnection</code>, <code>Get-CACrlDistributionPoint</code>, and <code>Get-ItemProperty</code> are the sources for that chain. Set the ignore flags everywhere, and you'll be turning them back off one app at a time, with the CRL still expired.</p>
<p>If certificates aren't arriving at all, start with <a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a>; its Step 9 is where this post picks up. If the failure is a Wi-Fi profile on Intune-managed devices, <a href="https://dev.to/adminpackstudio/intune-wi-fi-profile-not-applying-a-read-first-assignment-and-eap-triage-d1">Intune Wi-Fi Profile Not Applying</a> covers the profile side. This post covers <strong>"Windows clients, servers, or the CA itself fail to validate certificates from an AD CS hierarchy with 'the revocation server was offline' (0x80092013) or 'unable to check revocation' (0x80092012), and you need a read-first path before you turn off revocation checking, set ignore-offline flags, or republish over the top."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><p><strong>Read the chain before you touch the CA.</strong> <code>certutil -verify -urlfetch</code> on the failing certificate fetches every AIA and CDP URL in the chain and shows which one failed (Step 2). Microsoft's autoenrollment blog: "Verify that all certification authorities in the chain have valid CRL's published."</p>
</li>
<li><p><strong>Check NextUpdate on every CRL in the chain.</strong> Microsoft's CAPI2 guide: "If the current date is later than the date in the nextUpdate field in the CRL, it means that the CRL has expired." The offline root's CRL is the usual suspect, because nothing publishes it on a schedule (Steps 3, 9).</p>
</li>
<li><p><strong>Test from the context that fails.</strong> Microsoft's 0x80092013 article says to "verify that the CRL is downloadable from the system context". Services run as SYSTEM with WinHTTP proxy settings, not yours (Step 4).</p>
</li>
<li><p><strong>Publishing isn't delivering.</strong> Microsoft: <code>certutil -CRL</code> "writes new CRL data according to the CA configuration; it doesn't prove that a web server, replication process, or external transfer makes the new files available." (Step 5).</p>
</li>
<li><p><strong>A fixed CRL can still fail from cache.</strong> Microsoft's fix for the old-cache cause is <code>certutil -setreg chain\ChainCacheResyncFiletime @now</code> and <code>certutil -urlcache * delete</code> (Steps 7, 11).</p>
</li>
<li><p><strong>Ignore-offline flags are a stopgap with an owner and an end date</strong>, not a fix. The CA flag, the NPS keys, and the curl and Git switches all weaken validation (Step 10, common false starts).</p>
</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which app, error code, certificate, context, network, since when</td>
<td>"Certificates are broken" with no certificate exported</td>
</tr>
<tr>
<td>2. Chain read</td>
<td><code>certutil -verify -urlfetch</code> run on the failing certificate, in the failing context</td>
<td>Only the admin's browser was tested</td>
</tr>
<tr>
<td>3. CRL time-valid</td>
<td>Every CRL in the chain is before its NextUpdate</td>
<td>Root CRL expired over a weekend</td>
</tr>
<tr>
<td>4. CDP reachable</td>
<td>The client can resolve and reach the first usable CDP URL as the identity that validates</td>
<td>Works for the admin, fails as SYSTEM or over VPN</td>
</tr>
<tr>
<td>5. Location current</td>
<td>HTTP and LDAP locations serve the CRL the CA last generated</td>
<td>CA has a new CRL; the web server has last month's</td>
</tr>
<tr>
<td>6. CAPI2 read</td>
<td>The failing action is named</td>
<td>"Offline" with no detail</td>
</tr>
<tr>
<td>7. Caches</td>
<td>The client uses the current CRL, not a cached old one</td>
<td>Fixed on the server, still failing on clients</td>
</tr>
<tr>
<td>8. CA side</td>
<td>CRLs publish on schedule to every location; CDP settings match what certificates carry</td>
<td>Events 65/74 nobody reads; CDP URL changed last year</td>
</tr>
<tr>
<td>9. Offline root</td>
<td>The root's CRL was renewed and distributed before NextUpdate</td>
<td>The root has been off since its last CRL</td>
</tr>
<tr>
<td>10. Relying parties</td>
<td>Each app's revocation policy is known and unchanged</td>
<td>NPS keys flipped during the last outage and never flipped back</td>
</tr>
<tr>
<td>11. Change</td>
<td>Only after Steps 1-10 agree: one link, one change</td>
<td>Ignore flags everywhere, same expired CRL</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Which app, and its exact error</strong></td>
<td>0x80092013 and 0x80092012 are different results (table below); app wording varies, the code doesn't</td>
</tr>
<tr>
<td><strong>Which certificate</strong></td>
<td>Export it (public part only, <code>.cer</code>) from the app's server, the client's store, or the CA. Step 2 needs the file</td>
</tr>
<tr>
<td><strong>Which context validates it</strong></td>
<td>A service, NPS, the CA service, and scheduled tasks validate as SYSTEM or a service account; a browser validates as the user (our reading)</td>
</tr>
<tr>
<td><strong>Which network</strong></td>
<td>LAN, VPN, Wi-Fi onboarding VLAN, internet. A CDP reachable from one isn't proof for another</td>
</tr>
<tr>
<td><strong>Since when, and what changed</strong></td>
<td>A date matching a CRL's NextUpdate is the strongest lead; also CA renewal, web server or DNS change, proxy change, firewall change</td>
</tr>
</tbody></table>
<p>The codes, each from Microsoft's Winerror.h page:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Microsoft's text</th>
<th>Usually points at (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>0x80092013</td>
<td><code>CRYPT_E_REVOCATION_OFFLINE</code></td>
<td>"The revocation function was unable to check revocation because the revocation server was offline."</td>
<td>A CRL that couldn't be retrieved or isn't valid (Steps 3-7)</td>
</tr>
<tr>
<td>0x80092012</td>
<td><code>CRYPT_E_NO_REVOCATION_CHECK</code></td>
<td>"The revocation function was unable to check revocation for the certificate."</td>
<td>No usable revocation information at all, often no CDP; common from curl and Git on Windows (Step 10)</td>
</tr>
<tr>
<td>0x80092010</td>
<td><code>CRYPT_E_REVOKED</code></td>
<td>"The certificate is revoked."</td>
<td>Revocation worked: the certificate is revoked. Don't clear caches to make it go away</td>
</tr>
<tr>
<td>0x80090358</td>
<td><code>SEC_E_REVOCATION_OFFLINE_KDC</code></td>
<td>"The revocation status of the domain controller certificate used for smartcard authentication could not be determined."</td>
<td>Smart card or certificate logon: the DC certificate's CRL (Step 10)</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Everything certificate-based failed at the same time</td>
<td>One CRL high in the chain expired, usually the root's</td>
<td>Steps 2, 3, 9</td>
</tr>
<tr>
<td>CA service won't start, Event 100 or 48</td>
<td>The CA can't check its own chain: usually the parent CA's CRL (Microsoft's Event 100 page)</td>
<td>Steps 8, 9</td>
</tr>
<tr>
<td>Works on the LAN, fails over VPN or the internet</td>
<td>The CDP URL is internal-only, or LDAP-only</td>
<td>Step 4</td>
</tr>
<tr>
<td>Works for you, fails for the service</td>
<td>SYSTEM proxy, or a CDP that needs authentication</td>
<td>Step 4</td>
</tr>
<tr>
<td>Fails right after a CRL was republished</td>
<td>Stale web copy, or cached old CRL</td>
<td>Steps 5, 7</td>
</tr>
<tr>
<td>Base CRL fine, delta fails</td>
<td>Delta not served (IIS double escaping), or not published</td>
<td>Step 5</td>
</tr>
<tr>
<td>Pages slow, no error</td>
<td>Revocation fetches timing out (SharePoint's 15-second example)</td>
<td>Steps 4, 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the chain with <code>certutil -verify -urlfetch</code> (read-only)</h2>
<p>This is the one test that answers most of the questions at once. Microsoft's autoenrollment blog: "The client does not have a valid certificate revocation list (CRL) from the issuing CA that it can use to check if a certificate has been revoked." Its test is to run <code>certutil -v -verify -urlfetch</code> against the issuing CA's certificate. Microsoft's CDP article uses the same test for any certificate: "From each representative network zone, validate the test certificate and retrieve its revocation data". Run it on the certificate that fails, on the machine that fails:</p>
<pre><code class="language-console">:: Read-only (writes one local file). Verify the chain and fetch every AIA and CDP URL in it (file names are examples)
certutil -v -verify -urlfetch C:\Temp\failing.cer &gt; C:\Temp\verify-failing.txt

:: Read-only (writes one local file). The issuing CA's own certificate, from the CA (config string is an example)
certutil -config "CA01.contoso.com\Contoso Issuing CA" -ca.cert C:\Temp\issuing-ca.cer
certutil -verify -urlfetch C:\Temp\issuing-ca.cer
</code></pre>
<p>Microsoft's description of <code>-verify</code>: "Verifies a certificate, certificate revocation list (CRL), or certificate chain." Microsoft's caution on running it: "it will use the security and proxy settings of the user that runs it". An admin session that passes doesn't clear the SYSTEM account; Step 4 has the SYSTEM form.</p>
<p>How to read the output (structure from the sample output a user posted on Microsoft Q&amp;A, community; status wording is our reading):</p>
<table>
<thead>
<tr>
<th>Part of the output</th>
<th>What it tells you</th>
</tr>
</thead>
<tbody><tr>
<td>One <code>CertContext[0][n]</code> block per certificate</td>
<td><code>[0][0]</code> is the certificate you passed; the last one is the root</td>
</tr>
<tr>
<td><code>Certificate AIA</code> / <code>Certificate CDP</code> under each block</td>
<td>Each URL that block's certificate points at, with a status. <code>Verified</code> is good; anything else is the lead</td>
</tr>
<tr>
<td><code>Base CRL CDP</code></td>
<td>Where the CRL says its delta CRL lives (empty if the CA doesn't publish deltas)</td>
</tr>
<tr>
<td><code>CRL nn:</code> with <code>ThisUpdate</code> / <code>NextUpdate</code></td>
<td>The CRL that was used, and when it stops being valid</td>
</tr>
<tr>
<td>The last lines</td>
<td>A pass shows a completed message; a failure names the error code (our reading)</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only. Pull the lines that matter out of the saved output (pattern list is ours)
Select-String -Path C:\Temp\verify-failing.txt -Pattern 'CertContext|CDP|CRL \d|ThisUpdate|NextUpdate|Verified|Expired|Failed|0x8009' |
  Select-Object LineNumber, Line
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The root's block shows no CDP</td>
<td>Normal. Microsoft's CAPI2 guide: a CDP is necessary "for any certificate except for a root CA certificate"</td>
<td>The next block down</td>
</tr>
<tr>
<td>The issuing CA's block: CDP URL not <code>Verified</code></td>
<td>The root's CRL (it covers the issuing CA's certificate) is expired or unreachable</td>
<td>Steps 3, 9</td>
</tr>
<tr>
<td>The leaf's block: CDP URL not <code>Verified</code></td>
<td>The issuing CA's CRL is expired or unreachable</td>
<td>Steps 3-5</td>
</tr>
<tr>
<td><code>NextUpdate</code> in the past on any CRL</td>
<td>Expired. Republish it (Step 11)</td>
<td>Step 3</td>
</tr>
<tr>
<td>First CDP URL fails, a later one is <code>Verified</code></td>
<td>Validation can still pass, but slowly; Microsoft says Windows clients try them in order</td>
<td>Step 4</td>
</tr>
<tr>
<td>Passes for you, fails in the app</td>
<td>Different context, proxy, or network</td>
<td>Step 4</td>
</tr>
<tr>
<td>A block with no CDP and no OCSP that isn't the root</td>
<td>Nothing to check against: 0x80092012 territory</td>
<td>Step 8: the CA's CDP settings</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Which CRL, and is it still time-valid? (read-only)</h2>
<p>Every CA in the chain except the root's own certificate needs a current CRL: the root's CRL covers the issuing CA's certificate, and the issuing CA's CRL covers the leaf. Microsoft's CAPI2 guide on what expired looks like: "If the current date is later than the date in the nextUpdate field in the CRL, it means that the CRL has expired." Its fix is one line: "To fix this error, publish a time-valid CRL on the server."</p>
<p>Read the CRLs the clients actually download, not only the CA's copy. Take the HTTP URL from Step 2's output:</p>
<pre><code class="language-powershell"># Read-only (writes local files). Download each CRL from its HTTP CDP and read its validity (URLs are examples)
$crls = @{
  'issuing' = 'http://pki.contoso.com/pki/ContosoIssuingCA.crl'
  'root'    = 'http://pki.contoso.com/pki/ContosoRootCA.crl'
}
foreach ($name in $crls.Keys) {
  Invoke-WebRequest -Uri $crls[$name] -OutFile "C:\Temp\$name.crl" -UseBasicParsing
  "== $name"
  certutil -dump "C:\Temp\$name.crl" | Select-String -Pattern 'ThisUpdate|NextUpdate'
}
</code></pre>
<p><code>certutil -dump</code> "Dumps the configuration information or files." <code>certutil -URL</code> opens the same check interactively: it "Verifies certificate or CRL URLs." On the CA, read what the CA itself generated and what it thinks it published:</p>
<pre><code class="language-powershell"># Read-only. On the CA: the CRL files the CA writes locally (default CertEnroll path) and their timestamps
Get-ChildItem -Path "$env:SystemRoot\System32\CertSrv\CertEnroll" -Filter *.crl |
  Select-Object Name, Length, LastWriteTime
</code></pre>
<pre><code class="language-console">:: Read-only. On the CA: base and delta CRL publish status, then the CRL schedule values (Name* is certutil's prefix match)
certutil -CAInfo crlstatus
certutil -CAInfo deltacrlstatus
certutil -getreg ca\CRLPeriod*
certutil -getreg ca\CRLOverlap*
certutil -getreg ca\CRLDelta*
</code></pre>
<p><code>certutil -CAInfo</code> lists <code>crlstatus</code> as "CRL Publish Status" and <code>deltacrlstatus</code> as "Delta CRL Publish Status". The CRL period is how long each CRL is valid; the overlap is the head start clients get. Microsoft's two-tier PKI lab guide: "The overlap period for CRLs is the amount of time at the end of a published CRLs lifetime that a client can use to obtain a new CRL before the old CRL is considered unusable". And: "The default setting for this value is 10% of the CRL lifetime." Microsoft's CDP article keeps the two ideas apart: "A publishing interval and a CRL validity period are different settings."</p>
<p>LDAP CDPs live in AD. Read when each CDP object last changed (our form; <code>whenChanged</code> moves when a CRL is written there, our reading):</p>
<pre><code class="language-powershell"># Read-only. CRL distribution point objects in the configuration partition and when each last changed
$config = (Get-ADRootDSE).configurationNamingContext
Get-ADObject -SearchBase "CN=CDP,CN=Public Key Services,CN=Services,$config" `
  -Filter "objectClass -eq 'cRLDistributionPoint'" -Properties whenChanged |
  Select-Object DistinguishedName, whenChanged | Sort-Object whenChanged
</code></pre>
<p><code>certutil -dsCRL</code> "Displays DS CRLs." Add <code>-config</code> to point it at one CA (our reading of its options).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Root CRL past NextUpdate</td>
<td>Nobody renewed the offline root's CRL</td>
<td>Step 9</td>
</tr>
<tr>
<td>Issuing CRL past NextUpdate, CA running</td>
<td>The CA isn't publishing, or publishes somewhere clients don't read</td>
<td>Steps 5, 8</td>
</tr>
<tr>
<td>CertEnroll copy current, HTTP copy old</td>
<td>Publication worked, delivery didn't</td>
<td>Step 5</td>
</tr>
<tr>
<td>HTTP copy current, LDAP object old</td>
<td>LDAP publication failing (Events 65/74)</td>
<td>Step 8</td>
</tr>
<tr>
<td>All current, still failing</td>
<td>Reachability, context, or cache</td>
<td>Steps 4, 7</td>
</tr>
<tr>
<td>Overlap of zero or a few minutes</td>
<td>Clients get no head start; any delay becomes an outage</td>
<td>Step 11</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Can this client, in this context, reach the CDP? (read-only)</h2>
<p>Microsoft's 0x80092013 article lists five reasons a CRL location is unreachable, including "System context proxy is applied but unreachable and requires user authentication." and "CRL Location requires authentication before access is granted." Microsoft's CDP article puts the test plainly: "Test the exact CDP URL from that client network."</p>
<p><strong>Name and port first</strong>, from the failing machine:</p>
<pre><code class="language-powershell"># Read-only. The CDP host from Step 2: DNS answer, then TCP 80 (host name is an example)
$cdpHost = 'pki.contoso.com'
Resolve-DnsName -Name $cdpHost
Test-NetConnection -ComputerName $cdpHost -Port 80 -WarningAction SilentlyContinue |
  Select-Object ComputerName, RemoteAddress, TcpTestSucceeded
</code></pre>
<p><strong>Then the context.</strong> Services don't use your browser's proxy. Read the machine's WinHTTP proxy and the two hives Microsoft's article checks for the system context:</p>
<pre><code class="language-console">:: Read-only. WinHTTP proxy (what many services use), then the proxy values Microsoft's 0x80092013 article reads
netsh winhttp show proxy
reg query "HKU\.DEFAULT\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable
reg query "HKU\S-1-5-18\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer
reg query "HKU\S-1-5-18\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v AutoConfigURL
</code></pre>
<p>Then repeat Step 2 as SYSTEM. Sysinternals PsExec's <code>-s</code> switch will "Run the remote process in the System account." Run locally, it does the same on this machine (our reading):</p>
<pre><code class="language-console">:: Read-only (writes one local file). Step 2's test as SYSTEM on the failing machine
psexec -s cmd /c "certutil -verify -urlfetch C:\Temp\failing.cer &gt; C:\Temp\verify-system.txt"
</code></pre>
<p><strong>Then the URL list.</strong> Microsoft's PKI design page: "Remember that Windows clients always retrieve the list of URLs in sequential order until a valid CRL is retrieved." An LDAP URL first in the list costs a non-domain or off-network client a timeout before it reaches HTTP. The same page: "Use HTTP CDP locations to provide accessible CRL locations for clients running non-Windows operating systems." For Entra joined devices, Microsoft's Windows Hello for Business guide says the same: "Publish your certificate revocation list to a location that is available to Microsoft Entra joined devices, such as a web-based URL". Microsoft's CAPI2 guide rules one scheme out: "CryptoAPI does not support the FTP protocol for network retrieval."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Name doesn't resolve off-LAN</td>
<td>Internal-only DNS name in the CDP</td>
<td>Step 11: an HTTP name every zone can resolve, for new certificates (Step 8)</td>
</tr>
<tr>
<td>TCP 80 blocked from the VPN pool</td>
<td>Firewall rule missing for the CDP host</td>
<td>Network change for that one host and port</td>
</tr>
<tr>
<td>Passes as you, fails as SYSTEM</td>
<td>WinHTTP or system-context proxy</td>
<td>Step 11: proxy bypass for the CDP host</td>
</tr>
<tr>
<td>Only LDAP URLs in the CDP</td>
<td>Non-domain, Entra joined, and off-network clients can't use them</td>
<td>Step 8: add HTTP for new certificates</td>
</tr>
<tr>
<td>The proxy asks for credentials</td>
<td>SYSTEM can't answer (Microsoft's cause)</td>
<td>Proxy exception for the CDP host</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Does the web or LDAP location serve the current file? (read-only)</h2>
<p>The CA writing a CRL and a client getting it are separate jobs. Microsoft: "The CA's local generation result doesn't validate the delivery chain." Its monitoring advice: "At each HTTP endpoint, verify that the expected CRL file is current, complete, and readable by the intended clients." Compare the CA's file with what the web server hands out:</p>
<pre><code class="language-powershell"># Read-only. Same bytes on the CA and at the HTTP CDP? (file names are examples; C:\Temp\issuing.crl is Step 3's download)
Get-FileHash -Path "$env:SystemRoot\System32\CertSrv\CertEnroll\ContosoIssuingCA.crl", 'C:\Temp\issuing.crl' -Algorithm SHA256 |
  Select-Object Hash, Path
</code></pre>
<p>Different hashes with a current CA file means the copy job, the share, or the replication between web servers didn't run (our reading). Microsoft's lab guide grants Cert Publishers modify rights on the web server's pki folder so the CA can write there; its 0x80092013 article adds "check File System Permissions on the CRL and Delta CRL files."</p>
<p><strong>Delta CRLs and IIS.</strong> A delta CRL's file name carries a <code>+</code>. Microsoft's lab guide: "Allowing double escaping is needed if you are publishing Delta CRLs to IIS because the Delta CRL file contains a + symbol." IIS's request filtering reference: "If set to true, request filtering will allow URLs with doubly-escaped characters." and "The default value is false." Read the setting on the CDP's site or folder:</p>
<pre><code class="language-powershell"># Read-only. On the IIS server: double escaping on the CDP folder (site and folder names are examples)
Import-Module WebAdministration
Get-WebConfigurationProperty -PSPath 'IIS:\Sites\Default Web Site\pki' -Filter system.webServer/security/requestFiltering -Name allowDoubleEscaping
</code></pre>
<p>Microsoft's CDP article also asks for "the .crl MIME mapping required by your web-server policy" and for checking "the actual HTTP response after deployment". Step 3's <code>Invoke-WebRequest</code> download is that check for the base CRL; repeat it with the delta file name (the <code>+</code> one) from Step 2's <code>Base CRL CDP</code> line.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Hashes differ</td>
<td>The web copy is stale</td>
<td>Step 11: the copy or replication job</td>
</tr>
<tr>
<td>Base downloads, delta returns 404</td>
<td>Double escaping off, or the delta was never copied</td>
<td>Step 11: double escaping (Microsoft's lab form)</td>
</tr>
<tr>
<td>401 or 403 from the CDP URL</td>
<td>Anonymous read missing on the folder or site</td>
<td>Folder permissions</td>
</tr>
<tr>
<td>CRL downloads, <code>certutil -dump</code> can't parse it</td>
<td>The server returns something else (an error page, a captive portal)</td>
<td>The response itself</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Turn on CAPI2 logging and read the action that failed (read-only, one log switch)</h2>
<p>When <code>-urlfetch</code> passes and the app still fails, the CAPI2 log shows what the app's own validation did. Microsoft's CAPI2 guide on why the app's message isn't enough: the API "was designed to return an error code and string", so many network problems reach the app as revocation offline. Enable the log, make it bigger, reproduce once, export, and turn it off. Microsoft: "The default size for the event log is 1 MB." It recommends "at least 4 MB to capture relevant events".</p>
<pre><code class="language-console">:: Low-risk. Enable CAPI2 logging at 4 MB, reproduce the failure, export, then disable (Microsoft's wevtutil forms)
wevtutil sl Microsoft-Windows-CAPI2/Operational /ms:4194304
wevtutil sl Microsoft-Windows-CAPI2/Operational /e:true
:: reproduce the failure now, then:
wevtutil epl Microsoft-Windows-CAPI2/Operational C:\Temp\capi2.evtx
wevtutil sl Microsoft-Windows-CAPI2/Operational /e:false
</code></pre>
<pre><code class="language-powershell"># Read-only. CAPI2 errors, then the ones whose XML names a known revocation action (action list from Microsoft's guide; the filter is ours)
$capi = Get-WinEvent -LogName 'Microsoft-Windows-CAPI2/Operational' -MaxEvents 2000 -ErrorAction SilentlyContinue
$capi | Where-Object Level -eq 2 | Select-Object TimeCreated, Id, TaskDisplayName | Format-Table -AutoSize
$capi | Where-Object { $_.ToXml() -match 'CheckTimeValidity|CanRetrieveFromNetwork|CanCheckServerForUpdatedCrl|BadProxy|GetCrlOrOcspUrls|IsCrlSignatureValid|2EE7|2EFD' } |
  Select-Object TimeCreated, Id, TaskDisplayName
</code></pre>
<p>Microsoft's guide names the events to open: results are in <strong>CertVerifyRevocation</strong>, and "To find the cause, you need to look at the" <strong>CertRejectedRevocationInfo</strong> and <strong>CryptRetrieveObjectByUrlWire</strong> events nested under it. In <strong>CertGetCertificateChain</strong>, the status flags <code>CERT_TRUST_REVOCATION_STATUS_UNKNOWN</code> plus <code>CERT_TRUST_IS_OFFLINE_REVOCATION</code> mean the revocation information couldn't be retrieved or was invalid. The actions, each from Microsoft's guide:</p>
<table>
<thead>
<tr>
<th>Action or code in the event</th>
<th>Microsoft's meaning</th>
<th>Link in the chain</th>
</tr>
</thead>
<tbody><tr>
<td><code>CheckTimeValidity</code> (CertRejectedRevocationInfo)</td>
<td>The CRL is expired: now is past its nextUpdate</td>
<td>Steps 3, 9</td>
</tr>
<tr>
<td><code>IsCrlSignatureValid</code></td>
<td>"If the signature of the CRL cannot be verified"</td>
<td>The CRL file at that location is wrong or corrupt (Step 5)</td>
</tr>
<tr>
<td><code>GetCrlOrOcspUrls</code></td>
<td>No CDP and no OCSP URL in the certificate</td>
<td>Step 8</td>
</tr>
<tr>
<td><code>CanRetrieveFromNetwork</code></td>
<td>Back-off: "CAPI2 avoids contacting the server too often for repeated offline responses."</td>
<td>Wait for <code>EarliestOnlineTime</code>, or Step 7</td>
</tr>
<tr>
<td><code>BadProxy</code> (CryptRetrieveObjectByUrlWire)</td>
<td>The proxy is wrong; usually the first in the list</td>
<td>Step 4</td>
</tr>
<tr>
<td><code>2EE7</code></td>
<td><code>ERROR_WINHTTP_NAME_NOT_RESOLVED</code></td>
<td>DNS for the CDP or proxy host</td>
</tr>
<tr>
<td><code>2EFD</code></td>
<td><code>ERROR_WINHTTP_CANNOT_CONNECT</code></td>
<td>The CDP host's port, or the host is down</td>
</tr>
</tbody></table>
<p>Microsoft's summary for the network rows: "fix the underlying component (such as network, proxy settings, Web server, or LDAP) that is causing failure."</p>
<hr />
<h2>Step 7: Is the client using a cached old CRL? (read-only)</h2>
<p>Clients keep CRLs. Microsoft's 0x80092013 article: "The CRL is retrieved by clients who can then cache the CRL (based on the configured lifetime of the CRL)". So a CRL you republished an hour ago doesn't help a client that cached the old one, and its fourth cause is exactly that: "The CRL is reachable but the client is picking from an old cache." Read the cache before you clear it:</p>
<pre><code class="language-console">:: Read-only. Cached CRL URLs for the current user's context (certutil's CRL filter), saved for the ticket
certutil -urlcache CRL &gt; C:\Temp\urlcache-crl.txt
</code></pre>
<p>Microsoft's syntax note for <code>-URLCache</code>: <code>delete</code> "deletes relevant URLs from the current user's local cache." Run the read in the same context as the failure: as the user for a user app, and as SYSTEM (Step 4's PsExec form) for a service (our reading).</p>
<p>CAPI2's back-off is a second kind of cache. After repeated offline results it stops asking; the <code>CanRetrieveFromNetwork</code> event gives the <code>EarliestOnlineTime</code> it will try again (Step 6).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Cached CRL for the failing URL, and the server copy is newer</td>
<td>Client still holds the old one</td>
<td>Step 11: Microsoft's two cache commands, after the server side is fixed</td>
</tr>
<tr>
<td>No cached entry, still failing</td>
<td>Not a cache problem</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td><code>CanRetrieveFromNetwork</code> in CAPI2</td>
<td>Back-off after repeated failures</td>
<td>Fix the URL first, then Step 11</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: The CA side: publication, schedule, and CDP settings (read-only)</h2>
<p><strong>Did the CA publish?</strong> Microsoft's AD CS event list gives the CRL publication events in the Application log:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Microsoft's text (event list for AD CS)</th>
<th>Means (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>48</td>
<td>"Revocation status for a certificate in the chain for CA certificate %3 for %1 could not be verified because a server is currently unavailable."</td>
<td>The CA can't check its own chain (usually the parent's CRL)</td>
</tr>
<tr>
<td>49</td>
<td>"could not be verified because no information is available describing how to check the revocation status."</td>
<td>A certificate in the CA's chain has no usable CDP</td>
</tr>
<tr>
<td>65 / 66</td>
<td>"Active Directory Certificate Services could not publish a Base CRL for key %1 to the following location: %2." (66: Delta)</td>
<td>A publication location refused the write: path, share, or LDAP rights</td>
</tr>
<tr>
<td>67</td>
<td>"made %1 attempts to publish a CRL and will stop publishing attempts until the next CRL is generated."</td>
<td>Retries ran out; that location stays stale until the next CRL</td>
</tr>
<tr>
<td>74 / 75</td>
<td>The same base / delta failure "to the following location on server %4"</td>
<td>Same, for a named server</td>
</tr>
<tr>
<td>100</td>
<td>"Active Directory Certificate Services did not start: Could not load or verify the current CA certificate." (Microsoft's Event 100 page)</td>
<td>The CA won't start; the CRL for a CA above it is the common cause</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only. On the CA: chain, publication, and startup events, last 30 days (IDs from Microsoft's AD CS event list)
Get-WinEvent -FilterHashtable @{
  LogName = 'Application'; ProviderName = 'Microsoft-Windows-CertificationAuthority'
  Id = 48, 49, 65, 66, 67, 74, 75, 100; StartTime = (Get-Date).AddDays(-30)
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Id, Message | Format-List

# Read-only. Is Certificate Services running?
Get-Service -Name CertSvc | Select-Object Name, Status, StartType
</code></pre>
<p>A Microsoft Q&amp;A post shows what a failed LDAP publish looks like in practice: "could not publish a Base CRL for key 0 to the following location: ldap:///" with a logon failure code (community; the location is redacted in the post).</p>
<p><strong>Where does the CA publish, and what do certificates carry?</strong> Microsoft: the CA's CDP configuration "represents both publication and retrieval settings". Read both:</p>
<pre><code class="language-powershell"># Read-only. On the CA: every CDP and AIA entry with its flags (Microsoft's lab guide uses the same Format-List form)
Get-CACrlDistributionPoint | Format-List
Get-CAAuthorityInformationAccess | Format-List
</code></pre>
<p>Microsoft's cmdlet page lists the flags on each CDP entry: <code>PublishToServer</code>, <code>PublishDeltaToServer</code>, <code>AddToCertificateCdp</code>, <code>AddToFreshestCrl</code>, <code>AddToCrlCdp</code>, <code>AddToCrlIdp</code>, and <code>Uri</code>. <code>PublishToServer</code> is where the CA writes; <code>AddToCertificateCdp</code> is what clients get told. A URL with <code>AddToCertificateCdp</code> and nothing that copies the file there is a CDP that goes stale on its own (our reading).</p>
<p>The trap with CDP changes: "Changing a CDP URL affects only certificates issued after the change." Microsoft's design page says the same: "Modifying the URL for a CRL distribution point only affects newly issued certificates." Certificates issued before a change still point at the old URL. Microsoft's troubleshooting table for that case: "Keep the old CDP online and restore current CRL content there. A CDP configuration change doesn't repair old certificates."</p>
<p><strong>The CA's own startup.</strong> Microsoft's Event 100 page: AD CS "startup can fail if there are problems with availability, validity, and chain validation for the CA certificate." Its resolution includes: "If a CRL for a CA in the chain has expired, generate new base and delta CRLs on this CA and copy them to the required locations." Microsoft's lab guide is blunt about the subordinate: if it can't find its parent's CRLs, the service "will fail to start on the subordinate CA". For the issuing CA, the parent is usually the offline root, so Step 9 is the fix. Read the CA's current ignore flags before anyone changes them:</p>
<pre><code class="language-console">:: Read-only. On the CA: CRLFlags as set now (a stopgap someone set last year shows up here)
certutil -getreg ca\CRLFlags
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Events 65/74 for an LDAP location</td>
<td>The CA can't write its CDP object</td>
<td>Rights on the CDP container; Step 11</td>
</tr>
<tr>
<td>Events 65/74 for a file or UNC location</td>
<td>Share, NTFS, or path problem</td>
<td>The share and Cert Publishers rights (Step 5)</td>
</tr>
<tr>
<td>Event 100 or 48 at startup</td>
<td>The parent CA's CRL is expired or unreachable from the CA</td>
<td>Step 9</td>
</tr>
<tr>
<td><code>CRLF_REVCHECK_IGNORE_OFFLINE</code> already set</td>
<td>A stopgap from an earlier outage, hiding offline results on the CA</td>
<td>Fix the CRL, then remove it (Step 11)</td>
</tr>
<tr>
<td>CDP URL in certificates differs from where the CA publishes</td>
<td>Old certificates point at a location nobody updates</td>
<td>Keep the old location current (Microsoft)</td>
</tr>
<tr>
<td>No HTTP URL with <code>AddToCertificateCdp</code></td>
<td>Off-network and non-Windows clients can't fetch</td>
<td>Step 11: staged CDP add for new certificates</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: The offline root's CRL (read-only)</h2>
<p>The offline root is the one CA nobody turns on, so its CRL is the one that expires. Microsoft's CDP article: "For an offline root CA, avoid delta CRLs unless a documented requirement justifies them. Use a scheduled, witnessed process to publish a new base CRL, transfer it to every retrieval endpoint, and verify it from the networks that depend on the root." Microsoft's lab guide sets the root up that way in its <code>CAPolicy.inf</code>: <code>CRLPeriod=weeks</code>, <code>CRLPeriodUnits=26</code>, <code>CRLDeltaPeriodUnits=0</code>, and explains: "Setting the CRLDeltaPeriodUnits=0 in the CAPolicy.inf disables Delta CRL publishing, which is the appropriate setting for an offline Root CA."</p>
<p>Read the root CRL's dates without powering on the root. Step 3's download of the root CRL gives NextUpdate; so does the issuing CA's block in Step 2's output. Then read the copy the issuing CA keeps locally. Microsoft's lab guide adds the root CRL to the subordinate's local Root store during setup; <code>-store</code> lists that store's contents (our reading that the CRL entries show there):</p>
<pre><code class="language-console">:: Read-only. On the issuing CA: CRLs in the local machine Root store (where the lab guide's addstore puts the root CRL)
certutil -store Root &gt; C:\Temp\root-store.txt
</code></pre>
<p>A community answer on Microsoft Q&amp;A notes a stale root CRL in that store can keep a service failing after the HTTP copy is fixed. The same answer adds that "Things like MSCEP" (NDES) read it from there; if Intune SCEP broke at the same time, <a href="https://dev.to/adminpackstudio/intune-scep-certificate-profile-failed-a-read-first-assignment-and-ndes-triage-4ibc">Intune SCEP Certificate Profile Failed</a> covers the NDES side.</p>
<p>Two clock checks before you blame the CRL (our reading): the root's clock sets ThisUpdate and NextUpdate on the CRL it signs, and a root VM that boots once a year can come up with the wrong time. A client clock that's off by days can call a valid CRL expired, too. <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> covers the domain clients' side.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Root CRL past NextUpdate</td>
<td>Everything under the root fails revocation</td>
<td>Step 11: the offline root sequence</td>
</tr>
<tr>
<td>Root CRL valid for days, not months</td>
<td>The CRL period is short for a CA that's off</td>
<td>Step 11: raise it on the root, then publish</td>
</tr>
<tr>
<td>HTTP root CRL current, issuing CA's store copy old</td>
<td>The CA validates with the old copy</td>
<td>Step 11: addstore, then cache resync</td>
</tr>
<tr>
<td>Root CRL's ThisUpdate is in the future or far past</td>
<td>The root's clock was wrong when it signed</td>
<td>Fix the root's time, then republish</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: What each relying party does with "unknown" (read-only)</h2>
<p>The same expired CRL looks different in each app. Read each app's policy before you change it, so you know what you'd be turning off:</p>
<p><strong>NPS (802.1X Wi-Fi and VPN, EAP-TLS).</strong> Microsoft's NPS page lists four DWORDs under <code>HKLM\SYSTEM\CurrentControlSet\Services\RasMan\PPP\EAP\13</code>, and "The following keys are set to 0 by default." With <code>IgnoreRevocationOffline</code> at 0: "When the NPS can't connect to a server that stores a revocation list, the certificate fails the revocation check and authentication fails." Read them on every NPS server:</p>
<pre><code class="language-powershell"># Read-only. On each NPS server: the EAP-TLS revocation keys (missing = default 0)
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\RasMan\PPP\EAP\13' -ErrorAction SilentlyContinue |
  Select-Object IgnoreNoRevocationCheck, IgnoreRevocationOffline, NoRevocationCheck, NoRootRevocationCheck
</code></pre>
<p>Microsoft's 802.1X troubleshooting page puts revocation in its list of usual causes: "Examples include invalid certificate, expiration, chain verification failure, and revocation check failure."</p>
<p><strong>Smart card and certificate logon.</strong> Microsoft's smart card article: "The revocation check must succeed from both the client and the domain controller." It also requires that "Every CA Certificate except the root CA in the certificate chain contains a valid CDP extension in the certificate." The client-side code for the DC's certificate is 0x80090358 (Step 1). Microsoft's DC certificate requirements: "The certificate must have a CRL distribution-point extension that points to a valid certificate revocation list (CRL)." Run Step 2 on a DC's certificate from a client, and on the client's certificate from a DC.</p>
<p><strong>DirectAccess.</strong> Microsoft's article is the 0x80092013 source above; its read is <code>netsh interface http show interface</code> on the client.</p>
<p><strong>SharePoint and other apps that call out to public CRLs.</strong> Microsoft's SharePoint article describes the slow version: "If the online CRL server cannot be reached from the SharePoint server for some reason, the operation times out after 15 seconds by default." No error, just slow pages (our reading of that article for internal apps).</p>
<p><strong>curl and Git on Windows.</strong> Both use Schannel, Windows' own validation, so an internal or proxy-inspected certificate whose CDP isn't reachable fails there too, often as 0x80092012 (our reading). curl's manual describes <code>--ssl-revoke-best-effort</code>: "Ignore certificate revocation checks when they failed due to missing/offline distribution points for the revocation check lists." and warns about <code>--ssl-no-revoke</code>: "WARNING: this option loosens the SSL security, and by using this flag you ask for exactly that." Git's <code>http.schannelCheckRevoke</code>: "Only necessary to disable this if Git consistently errors and the message is about checking the revocation status of a certificate." Run Step 2 on the server certificate first; if a TLS-inspecting proxy re-signs traffic, the CDP that fails is the proxy CA's (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>NPS keys all 0 or missing</td>
<td>Default: NPS fails closed when a CRL is unavailable</td>
<td>Fix the CRL (Steps 3-9)</td>
</tr>
<tr>
<td><code>NoRevocationCheck</code> = 1 already</td>
<td>Revocation is off on that NPS server; revoked certificates connect</td>
<td>Find who set it and why, then plan to remove it</td>
</tr>
<tr>
<td>Smart card logon fails, password logon works</td>
<td>DC or client can't check the other's chain</td>
<td>Step 2 in both directions</td>
</tr>
<tr>
<td>Only curl / Git fail</td>
<td>Schannel can't reach the CDP of that certificate's issuer</td>
<td>Step 2 on the server certificate; Step 4</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><code>CRLF_REVCHECK_IGNORE_OFFLINE</code> <strong>on the CA, left on</strong></td>
<td>Community answers on Microsoft Q&amp;A use it to get a CA started; left on, the CA stops caring whether its chain's CRLs are reachable (our view)</td>
<td>Step 9's root CRL first; if you must use it, Step 11's time-boxed form</td>
</tr>
<tr>
<td><code>NoRevocationCheck</code> <strong>= 1 on NPS</strong></td>
<td>Microsoft: with it enabled, NPS "prevents EAP-TLS from performing a revocation check of the certificate of the client." A revoked laptop certificate connects</td>
<td>Fix the CRL; if you need a bridge, Step 11</td>
</tr>
<tr>
<td><strong>Revocation off in the VPN or app profile</strong></td>
<td>Every client now needs a second change to undo it (our view)</td>
<td>Step 10 for that app</td>
</tr>
<tr>
<td><strong>Changing the CDP URL on the CA to repair old certificates</strong></td>
<td>Microsoft: "Changing a CDP URL affects only certificates issued after the change."</td>
<td>Restore current content at the old URL (Step 8)</td>
</tr>
<tr>
<td><strong>Republishing on the CA and declaring it fixed</strong></td>
<td>Publishing isn't delivering; clients may hold the old one</td>
<td>Steps 5, 7</td>
</tr>
<tr>
<td><strong>Renewing the CA certificate</strong></td>
<td>A new CA certificate doesn't renew the parent's CRL that failed (our reading)</td>
<td>Step 9</td>
</tr>
<tr>
<td><code>certutil -urlcache * delete</code> <strong>on every machine, repeatedly</strong></td>
<td>Clears the symptom for an hour if the server copy is still old (our reading)</td>
<td>Fix the server copy, then clear once</td>
</tr>
<tr>
<td><strong>Delta CRLs on the offline root</strong></td>
<td>Microsoft: "Avoid publishing delta CRLs on offline root CAs." Each one means powering on the root</td>
<td>Base CRL only on the root (Step 9)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 11: Change one link, then prove it (only after the read)</h2>
<p>Pick the one change Steps 1-10 point at. Save the current state first:</p>
<pre><code class="language-powershell"># Low-risk write. Save the CA configuration, CDP/AIA settings, and current CRL files (our habit; run on the CA)
New-Item -Path C:\Temp\pki-before -ItemType Directory -Force | Out-Null
reg export 'HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration' C:\Temp\pki-before\certsvc-config.reg /y
Get-CACrlDistributionPoint | Format-List &gt; C:\Temp\pki-before\cdp.txt
Get-CAAuthorityInformationAccess | Format-List &gt; C:\Temp\pki-before\aia.txt
Copy-Item -Path "$env:SystemRoot\System32\CertSrv\CertEnroll\*.crl" -Destination C:\Temp\pki-before
</code></pre>
<p><strong>Expired issuing CA CRL (Step 3).</strong> On the CA, Microsoft's scripted form:</p>
<pre><code class="language-console">:: Change, on the issuing CA. Publish new base (and delta, if configured) CRLs, then read the status back
certutil -config "CA01.contoso.com\Contoso Issuing CA" -CRL
certutil -CAInfo crlstatus
</code></pre>
<p>Microsoft: <code>certutil -CRL</code> "Publishes new certificate revocation lists (CRLs) or delta CRLs." Then deliver it. If no job copies the file, copy it to every HTTP location yourself (Microsoft's server-certificate guide uses a plain copy to the web server's share), and rerun Step 5's hash check:</p>
<pre><code class="language-powershell"># Change. Copy the CA's CRLs to the web server's pki share (share name is an example; Microsoft's guide uses \\WEB1\pki)
Copy-Item -Path "$env:SystemRoot\System32\CertSrv\CertEnroll\*.crl" -Destination '\\WEB01\pki' -WhatIf
</code></pre>
<p><strong>Expired offline root CRL (Step 9).</strong> Microsoft's Event 100 page and lab guide, in order (the order and the dry-run habits are ours):</p>
<ol>
<li><p>Power on the root. Check its clock before anything else (Step 9).</p>
</li>
<li><p>If the CRL period is too short for how often you'll do this, change it first. Microsoft's lab guide sets the root's values with <code>certutil -setreg</code>, restarts the service, and publishes; one example:</p>
</li>
</ol>
<pre><code class="language-powershell"># Change, on the offline root (elevated PowerShell). Longer CRL validity, 12-hour overlap, restart, publish (Microsoft's lab form; values are examples)
certutil -setreg CA\CRLPeriodUnits 26
certutil -setreg CA\CRLPeriod "Weeks"
certutil -setreg CA\CRLOverlapPeriodUnits 12
certutil -setreg CA\CRLOverlapPeriod "Hours"
Restart-Service -Name CertSvc
certutil -crl
</code></pre>
<ol>
<li><p>Copy the new CRL from <code>%windir%\System32\CertSrv\CertEnroll</code> to removable media, then power the root off.</p>
</li>
<li><p>On a domain-joined machine, publish it to AD. <code>-dspublish</code> "Publishes a certificate or certificate revocation list (CRL) to Active Directory." and "Use -f to create a new DS object." Its container argument "is the DS CDP container CN, usually the CA machine name."</p>
</li>
<li><p>Copy it to every HTTP CDP location, and add it to the issuing CA's local store the way Microsoft's lab guide does.</p>
</li>
<li><p>Rerun Step 2 on the issuing CA's certificate, then start the issuing CA if it was down.</p>
</li>
</ol>
<pre><code class="language-console">:: Change, on a domain member (the issuing CA works). Root CA machine name ORCA1 and file names are examples
certutil -f -dspublish C:\Temp\ContosoRootCA.crl ORCA1
copy C:\Temp\ContosoRootCA.crl \\WEB01\pki\
certutil -addstore -f root C:\Temp\ContosoRootCA.crl
certutil -verify -urlfetch C:\Temp\issuing-ca.cer
</code></pre>
<p>Microsoft's lab guide on that last store step: "The second and third commands place the root CA certificate and CRL into the local store of APP1." Then put the next NextUpdate, minus the overlap, on a calendar with an owner. Microsoft: "Alert before a CRL reaches its next-update boundary."</p>
<p><strong>A CA that won't start while you fix the root (Step 8).</strong> The fix is the parent's CRL. If issuance has to resume first, community answers on Microsoft Q&amp;A use the ignore-offline flag, then remove it once the CRL is valid (the accepted answer also resyncs the chain cache before removing it). Treat it as a time-boxed exception with an owner (our view):</p>
<pre><code class="language-powershell"># Change, on the issuing CA (stopgap, elevated PowerShell). Read, set, restart; flag form from Microsoft Q&amp;A community answers
certutil -getreg ca\CRLFlags
certutil -setreg ca\CRLFlags +CRLF_REVCHECK_IGNORE_OFFLINE
Restart-Service -Name CertSvc

# After the root CRL is fixed and Step 2 passes on the CA: resync the chain cache, remove the flag, restart
# ('@now' is quoted because PowerShell would read a bare @now as splatting)
certutil -setreg chain\ChainCacheResyncFiletime '@now'
certutil -setreg ca\CRLFlags -CRLF_REVCHECK_IGNORE_OFFLINE
Restart-Service -Name CertSvc
</code></pre>
<p><strong>Unreachable CDP (Step 4).</strong> One change for the one cause: a DNS record, a firewall rule from that network to the CDP host on TCP 80, or a proxy bypass for the CDP host so SYSTEM doesn't need proxy credentials. Then rerun Step 4 from that network and context.</p>
<p><strong>Stale web copy or delta 404 (Step 5).</strong> Fix the copy job first. For deltas on IIS, Microsoft's 0x80092013 article gives the double-escaping setting; dry-run it on the CDP folder only:</p>
<pre><code class="language-powershell"># Change, on the IIS server. Allow double escaping on the CDP folder only (site and folder are examples; Microsoft's KB form plus -WhatIf)
Set-WebConfiguration -Filter system.webServer/security/requestFiltering -PSPath 'IIS:\Sites\Default Web Site\pki' -Value @{ allowDoubleEscaping = $true } -WhatIf
</code></pre>
<p><strong>Cached old CRL (Step 7).</strong> Only after the server copy is current. Microsoft's two commands, from the 0x80092013 article and the NPS page ("To manually update the CRL on your NPS server, run these commands in the command prompt or PowerShell"), in the context that fails:</p>
<pre><code class="language-console">:: Change (local caches only). Clear cached URLs and force chain revalidation; run as the failing context (SYSTEM via psexec -s for services)
certutil -urlcache * delete
certutil -setreg chain\ChainCacheResyncFiletime @now
</code></pre>
<p><strong>CDP changes for new certificates (Step 8).</strong> Microsoft's article is the procedure: "Use a staged transition. Add and validate the new endpoint before you remove an old one." <code>Add-CACrlDistributionPoint</code> "prompts for confirmation". For removal with <code>Remove-CACrlDistributionPoint</code>, use <code>-WhatIf</code> and the matching flags: "If you omit the flags, the cmdlet removes every distribution point that matches the URI."</p>
<pre><code class="language-powershell"># Change, on the CA. Add an HTTP URL to the CDP of newly issued certificates (Microsoft's example; prompts unless -Force)
Add-CACrlDistributionPoint -Uri "http://pki.contoso.com/pki/&lt;CAName&gt;&lt;CRLNameSuffix&gt;&lt;DeltaCRLAllowed&gt;.crl" -AddToCertificateCdp -AddToFreshestCrl
</code></pre>
<p>Restart the CA if the cmdlet says so, publish new CRLs, issue one test certificate, and run Step 2 on it.</p>
<p><strong>A bridge on NPS while the CRL is fixed (Step 10).</strong> Microsoft documents <code>IgnoreRevocationOffline</code>: "When enabled, the NPS allows EAP-TLS clients to connect even when a server that stores a CRL isn't available on the network". It's narrower than <code>NoRevocationCheck</code>: a certificate that's on a CRL NPS can read is still rejected (our reading). If you use it, use it on purpose, write down who owns setting it back to 0, and do it on every NPS server the same way (our view).</p>
<p><strong>Then prove it.</strong> Microsoft's validation step: "Confirm that the output shows a successful retrieval of the base CRL and, when you use delta CRLs, of the associated delta CRL." From each network zone, as the user and as SYSTEM:</p>
<pre><code class="language-console">:: Read-only (writes local files). Step 2 again from each zone and context; then confirm with the app itself
certutil -verify -urlfetch C:\Temp\failing.cer &gt; C:\Temp\verify-after.txt
psexec -s cmd /c "certutil -verify -urlfetch C:\Temp\failing.cer &gt; C:\Temp\verify-after-system.txt"
</code></pre>
<p>Then the thing the user waited for: one laptop on the 802.1X SSID, one VPN connection, one smart card logon. Turn CAPI2 logging off if it's still on (Step 6), and remove any stopgap flag you set (our habit).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Root CRL expired</td>
<td>Root on, <code>certutil -crl</code>, publish to AD and HTTP, store copy on the issuing CA</td>
<td>Ignore flags on every relying party</td>
</tr>
<tr>
<td>Issuing CRL stale at HTTP</td>
<td>Fix the copy job; hash-check the web copy</td>
<td>Republishing again and again on the CA</td>
</tr>
<tr>
<td>CDP unreachable from one zone</td>
<td>DNS, firewall, or proxy bypass for that host</td>
<td>Turning off revocation for that zone's clients</td>
</tr>
<tr>
<td>Client still on the old CRL</td>
<td>Clear its cache once, after the server is current</td>
<td>Scheduled cache wipes on every machine</td>
</tr>
<tr>
<td>CA down during the root fix</td>
<td>Time-boxed <code>CRLF_REVCHECK_IGNORE_OFFLINE</code>, then remove it</td>
<td>Leaving it set after the CRL is fixed</td>
</tr>
<tr>
<td>Old certificates with a dead URL</td>
<td>Keep that URL serving current CRLs</td>
<td>Changing the CA's CDP and expecting old certificates to follow</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does "the revocation server was offline" mean?</strong> A client building a certificate chain couldn't get valid revocation information for one of the certificates: usually an expired CRL, a CDP URL it can't reach in its context, a CRL that was never published, or an old cached copy. Those are the four causes in Microsoft's 0x80092013 article. The error code is 0x80092013, <code>CRYPT_E_REVOCATION_OFFLINE</code>.</p>
<p><strong>How do I find which CRL is expired?</strong> Run <code>certutil -verify -urlfetch</code> on the failing certificate from the failing machine, then read <code>NextUpdate</code> on each CRL it fetched. Download the CRL from its HTTP URL and run <code>certutil -dump</code> on it to confirm. The offline root's CRL is the first one to check.</p>
<p><strong>How do I renew an offline root CA's CRL?</strong> Power on the root, run <code>certutil -crl</code>, copy the new CRL off on removable media, publish it to AD with <code>certutil -f -dspublish</code>, copy it to every HTTP CDP location, and add it to the issuing CA's store with <code>certutil -addstore -f root</code>. Then verify from the issuing CA with <code>certutil -verify -urlfetch</code>, and set a reminder before the next NextUpdate.</p>
<p><strong>Why does it still fail after I published a new CRL?</strong> Publishing on the CA doesn't copy the file to the web server, and clients keep cached CRLs. Compare the web copy's hash with the CA's file, then clear the failing context's cache with <code>certutil -urlcache * delete</code> and <code>certutil -setreg chain\ChainCacheResyncFiletime @now</code>.</p>
<p><strong>What's the difference between 0x80092012 and 0x80092013?</strong> 0x80092013 is "revocation server was offline": revocation information exists but couldn't be retrieved or isn't valid. 0x80092012 is "unable to check revocation for the certificate", which is common when a certificate has no usable CDP at all, or from curl and Git on Windows. Both start with Step 2.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><p><a href="https://dev.to/adminpackstudio/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage-4h86">Certificate Autoenrollment Not Working</a> (the AD CS sibling: autoenrollment's revocation error is Step 9 there)</p>
</li>
<li><p><a href="https://dev.to/adminpackstudio/intune-wi-fi-profile-not-applying-a-read-first-assignment-and-eap-triage-d1">Intune Wi-Fi Profile Not Applying</a> (EAP-TLS Wi-Fi, often the first thing an expired CRL breaks)</p>
</li>
<li><p><a href="https://dev.to/adminpackstudio/intune-scep-certificate-profile-failed-a-read-first-assignment-and-ndes-triage-4ibc">Intune SCEP Certificate Profile Failed</a> (NDES and the connector depend on the same chain)</p>
</li>
<li><p><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (a wrong clock can make a valid CRL look expired)</p>
</li>
</ul>
<h2>Free checklist</h2>
<p>An ignore-revocation flag pushed to every NPS server before one laptop was tested against a fixed CRL is a change nobody tested where the validation happens. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm%5C_source=hashnode&amp;utm%5C_medium=article&amp;utm%5C_campaign=aps%5C_first%5C_sale&amp;utm%5C_content=revocation-server-offline-crl-expired">https://cashflow4375.gumroad.com/l/ujkqxb?utm\_source=hashnode&amp;utm\_medium=article&amp;utm\_campaign=aps\_first\_sale&amp;utm\_content=revocation-server-offline-crl-expired</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm%5C_source=hashnode&amp;utm%5C_medium=article&amp;utm%5C_campaign=aps%5C_first%5C_sale&amp;utm%5C_content=revocation-server-offline-crl-expired">https://cashflow4375.gumroad.com/l/joonf?utm\_source=hashnode&amp;utm\_medium=article&amp;utm\_campaign=aps\_first\_sale&amp;utm\_content=revocation-server-offline-crl-expired</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-10 favor read-only checks:</em> <code>certutil -verify -urlfetch</code><em>,</em> <code>-ca.cert</code><em>,</em> <code>-dump</code><em>,</em> <code>-URL</code><em>,</em> <code>-CAInfo</code><em>,</em> <code>-getreg</code><em>,</em> <code>-dsCRL</code><em>,</em> <code>-urlcache CRL</code><em>,</em> <code>-store</code><em>,</em> <code>Select-String</code><em>,</em> <code>Invoke-WebRequest</code><em>,</em> <code>Get-ChildItem</code><em>,</em> <code>Get-ADRootDSE</code><em>,</em> <code>Get-ADObject</code><em>,</em> <code>Resolve-DnsName</code><em>,</em> <code>Test-NetConnection</code><em>,</em> <code>netsh winhttp show proxy</code><em>,</em> <code>reg query</code><em>,</em> <code>Get-FileHash</code><em>,</em> <code>Get-WebConfigurationProperty</code><em>,</em> <code>Get-WinEvent</code><em>,</em> <code>Get-Service</code><em>,</em> <code>Get-CACrlDistributionPoint</code><em>,</em> <code>Get-CAAuthorityInformationAccess</code><em>, and</em> <code>Get-ItemProperty</code><em>. Output redirection, downloads,</em> <code>wevtutil epl</code><em>, and</em> <code>reg export</code> <em>write local files;</em> <code>psexec -s</code> <em>runs the same reads as SYSTEM. CAPI2 logging,</em> <code>certutil -CRL</code><em>, CRL period and overlap values,</em> <code>-dspublish</code><em>,</em> <code>-addstore</code><em>, file copies to CDP locations,</em> <code>CRLFlags</code><em>,</em> <code>Restart-Service CertSvc</code><em>, cache clears, IIS double escaping, proxy and firewall changes, CDP changes, and NPS registry keys are deliberate changes. Change one link at a time, test from each network zone and context, and check current Microsoft Learn for version-specific behavior.</em></p>
]]></content:encoded></item><item><title><![CDATA[DNS Scavenging Not Working: A Read-First Server, Zone Aging, Record Timestamp, and Replication Triage]]></title><description><![CDATA[DNS scavenging not working? Read the server's scavenging interval, zone aging, record timestamps, events 2501/2502, and replication first.]]></description><link>https://adminpackstudio.hashnode.dev/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/dns-scavenging-not-working-a-read-first-server-zone-aging-record-timestamp-and-replication-triage</guid><category><![CDATA[sysadmin]]></category><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[dns]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 16:44:55 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "RDP to FIN-LT-114 keeps connecting to the wrong laptop and failing with a logon error. <code>nslookup</code> for the Finance print server returns three addresses, and two of them belong to laptops that were reimaged in the spring. Somebody turned on scavenging on the zone last year and it's 'not working'. A forum post says to run <code>dnscmd /ageallrecords</code> on the zone and set the refresh intervals to one hour on every domain controller. Can we just do that?"</p>
<p>Not yet. <strong>Scavenging isn't one switch. A record is only deleted when four things are true at once, on the server doing the scavenging: scavenging is on for that server, aging is on for the zone, the record carries a timestamp, and that timestamp is older than the no-refresh plus refresh intervals.</strong> Microsoft's own list: "Scavenging removes a record only when it meets all four of the following conditions". And the reason nothing happens when only half of it is set: "Aging alone never deletes records; it only tracks timestamps." <code>dnscmd /ageallrecords</code> stamps every record in the zone, including the static ones you meant to keep, and Microsoft says that stamp "can't be reversed." One-hour intervals on every DC turn a slow cleanup into a fast one that can delete live records. Work through it as a chain: <strong>which records, in which zone, since when</strong> → <strong>is scavenging on for a server, and which one?</strong> → <strong>is aging on for the zone, and is the zone eligible yet?</strong> → <strong>do the stale records have a timestamp at all?</strong> → <strong>what do events 2501 / 2502 and the audit log say happened?</strong> → <strong>is something still refreshing the "stale" record (a client, DHCP, a second NIC)?</strong> → <strong>for AD-integrated zones: has the partition replicated, and which server is allowed to scavenge?</strong> → <strong>the reverse problem: scavenging deleting live records</strong>. <code>Get-DnsServerScavenging</code>, <code>Get-DnsServerZoneAging</code>, <code>dnscmd /zoneinfo</code>, <code>Get-DnsServerResourceRecord</code>, <code>Get-WinEvent</code>, and <code>Get-DhcpServerv4DnsSetting</code> are the sources for that chain. Stamp every record and shorten every interval on a broken chain, and you've traded stale records for missing ones.</p>
<p>If the stale record showed up as AD replication failing with error 1722 because a DC's A record points at an old address, read <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> first; fix that one record there, then come back for the cleanup. If clients aren't getting addresses at all, start with <a href="https://dev.to/adminpackstudio/windows-server-dhcp-not-assigning-ip-addresses-a-read-first-scope-authorization-relay-and-d13">Windows Server DHCP Not Assigning IP Addresses</a>. This post covers <strong>"Stale A or PTR records stay in a Windows Server DNS zone (usually AD-integrated) after aging and scavenging were supposedly enabled, or scavenging ran and removed records it shouldn't have, and you need a read-first path before you stamp records, shorten intervals, or turn scavenging on everywhere."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Check all three levels: server, zone, record.</strong> Microsoft: "For scavenging to take effect, it must be configured on three levels: record, zone, and server." Server-level scavenging doesn't replicate; zone aging does (Steps 2-4).</li>
<li><strong>A scavenging interval of 0 means off.</strong> On <code>Set-DnsServerScavenging</code>, Microsoft: "The default setting is 0, which disables scavenging for the DNS server." Read it on every DNS server, not only the one you remember configuring (Step 2).</li>
<li><strong>Static records never age.</strong> "The timestamp of static records is 0, indicating they aren't scavenged." Records created before aging was turned on may carry no timestamp either (Step 4).</li>
<li><strong>Event 2502 isn't always "all clean".</strong> Its message: "The DNS server has completed a scavenging cycle but no nodes were visited," and the first cause Microsoft lists is "No zones are configured for scavenging by this server." (Step 5).</li>
<li><strong>A zone can name a scavenging server that no longer exists.</strong> Then the cycle runs and deletes nothing. <code>dnscmd /zoneinfo</code> shows the list; <code>dnscmd /zoneresetscavengeservers</code> changes it (Steps 3, 9).</li>
<li><strong>Defaults are slow on purpose.</strong> With 7-day intervals and a 7-day scavenging period, Microsoft says a stale record can stay "up to 21 days with all defaults." Patience is part of the fix.</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which records, which zone, which servers, since when, what changed</td>
<td>"DNS is full of junk" with no record name</td>
</tr>
<tr>
<td>2. Server scavenging</td>
<td>One DNS server per zone has scavenging on, with a nonzero interval</td>
<td>Interval 0 everywhere; or on every DC with different schedules</td>
</tr>
<tr>
<td>3. Zone aging</td>
<td>Aging on for the zone; zone eligible; this server allowed to scavenge it</td>
<td>Aging off; "can be scavenged after" in the future; scavenge server list names a dead DC</td>
</tr>
<tr>
<td>4. Record timestamp</td>
<td>Stale records carry a nonzero timestamp older than both intervals</td>
<td>Old records with timestamp 0 (static)</td>
</tr>
<tr>
<td>5. Events</td>
<td>2501 or 2502 read on the scavenging server; audit 521 shows what was deleted</td>
<td>2502 "no nodes were visited"; no 2501/2502 at all</td>
</tr>
<tr>
<td>6. Refresh source</td>
<td>Nothing still refreshes the stale name</td>
<td>A client on VPN, a second NIC, or DHCP keeps it fresh; DHCP owns the record</td>
</tr>
<tr>
<td>7. AD replication</td>
<td>The zone's partition has replicated since the DNS service started</td>
<td>No 2501/2502 events; servers disagree on the record</td>
</tr>
<tr>
<td>8. Over-deletion</td>
<td>Intervals at least as long as the longest refresh period</td>
<td>Live servers lose records every few days</td>
</tr>
<tr>
<td>9. Change</td>
<td>Only after Steps 1-8 agree: one link, one change</td>
<td><code>/ageallrecords</code> on the zone, same problem plus static records at risk</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Two or three record names that should be gone</strong> (and their timestamps)</td>
<td>You'll trace these exact records through every step (our habit)</td>
</tr>
<tr>
<td><strong>Zone name and type</strong> (forward or reverse, AD-integrated or file-backed)</td>
<td>Aging is per zone. "Secondary and stub zones don't support these processes."</td>
</tr>
<tr>
<td><strong>Every DNS server that hosts the zone</strong></td>
<td>Server-level scavenging is set per server and doesn't replicate</td>
</tr>
<tr>
<td><strong>When scavenging was "turned on", and by whom</strong></td>
<td>A recent change may still be inside its first refresh interval (Step 3)</td>
</tr>
<tr>
<td><strong>What changed</strong></td>
<td>DC retired, DNS role moved, zone converted to AD-integrated, DHCP lease or scope changes, DHCP failover added</td>
</tr>
<tr>
<td><strong>The opposite symptom?</strong></td>
<td>Records for live machines disappearing is a different branch (Step 8)</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>No 2501 or 2502 events on any server</td>
<td>No server is scavenging, or the zone's partition hasn't replicated</td>
<td>Steps 2, 7</td>
</tr>
<tr>
<td>2502 every cycle on the server you configured</td>
<td>Microsoft's first cause: "No zones are configured for scavenging by this server."</td>
<td>Step 3</td>
</tr>
<tr>
<td>2501 runs, the old records survive</td>
<td>Records without a timestamp, or a different server is the only one allowed to scavenge</td>
<td>Steps 3, 4</td>
</tr>
<tr>
<td>Old records with a recent timestamp</td>
<td>Something still refreshes them</td>
<td>Step 6</td>
</tr>
<tr>
<td>Two names on one IP address</td>
<td>Lease reused before the old record aged out (Microsoft's DHCP lease blog)</td>
<td>Step 6</td>
</tr>
<tr>
<td>Records for live servers vanish</td>
<td>Intervals shorter than the refresh period, or stamped static records</td>
<td>Step 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Is scavenging on for a server, and which one? (read-only)</h2>
<p>Server-level scavenging is a property of each DNS server, not of the zone. Microsoft's configuration page: "This setting doesn't replicate, so configure it individually on every server that needs to scavenge." Read it on every DNS server that hosts the zone. <code>nltest /dclist:</code> lists the DCs (Microsoft uses it in its scavenging article):</p>
<pre><code class="language-console">:: Read-only. List the domain controllers (contoso.com is an example domain)
nltest /dclist:contoso.com

:: Read-only. One server-level setting from the registry (Microsoft's /info syntax; DC01 is an example)
dnscmd DC01 /info /scavenginginterval
</code></pre>
<pre><code class="language-powershell"># Read-only. Server-level aging and scavenging settings on each DNS server (server names are examples; the loop is our form)
foreach ($dc in 'DC01', 'DC02', 'DC03') {
  "=== $dc"
  Get-DnsServerScavenging -ComputerName $dc | Format-List *
}
</code></pre>
<p>Read <code>ScavengingInterval</code> first. Microsoft on that parameter: it "determines whether the scavenging feature for the DNS server is enabled and sets the number of hours between scavenging cycles." And: "The default setting is 0, which disables scavenging for the DNS server." The <code>dnscmd</code> page says the same for <code>/scavenginginterval</code>: "The default setting is 0x0, which disables scavenging for the DNS server." <code>ScavengingState</code> is a different thing; its parameter page says it "determines whether the DNS scavenging feature is enabled by default on newly created zones." The output also shows a last-scavenge time on the builds we've seen; the cmdlet page doesn't document the output fields, so read the <code>Format-List *</code> output rather than trusting a property name (our note).</p>
<p><strong>How many servers should scavenge?</strong> Microsoft: "Enable server-level scavenging on only one DNS server per zone." And on why one is enough: "Only one server is required to scavenge since the zone data is replicated to all servers hosting the zone." Microsoft's dynamic update troubleshooting guide adds where to put it: "The scavenging cycle is a low-priority thread of the DNS process, so it might not always run consistently." Its advice: "Ideally, scavenging should be set up on a DC without any FSMO roles."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Interval 0 on every server</td>
<td>Nobody scavenges. Zone aging alone deletes nothing</td>
<td>Step 9: one server</td>
</tr>
<tr>
<td>Nonzero on one server</td>
<td>Expected</td>
<td>Step 3: is that server allowed to scavenge the zone?</td>
</tr>
<tr>
<td>Nonzero on several servers</td>
<td>Works, but "Scavenging events and audit data spread across multiple servers."</td>
<td>Read events on each (Step 5); reduce to one (Step 9)</td>
</tr>
<tr>
<td>Nonzero only on a retired or PDC emulator DC</td>
<td>Retired: nobody scavenges. PDC: low-priority thread on a busy DC</td>
<td>Step 9: move it to a DC without FSMO roles</td>
</tr>
<tr>
<td>Interval very short (hours)</td>
<td>Not a cause of "not working"; a cause of surprises later (our reading)</td>
<td>Step 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Is aging on for the zone, and is the zone eligible yet? (read-only)</h2>
<p>Aging is the zone half. For AD-integrated zones it replicates with the zone: "For an AD-integrated zone, enable aging once and the setting replicates with the zone data to other DNS servers that host the zone." Microsoft also says the zone setting wins over the server-wide default: "The zone-level aging has higher precedence." Read both the PowerShell view and the registry view on the server that should scavenge:</p>
<pre><code class="language-powershell"># Read-only. Zone type and aging settings for one zone on the scavenging server (zone and server names are examples)
Get-DnsServerZone -Name 'contoso.com' -ComputerName DC02 | Format-List *
Get-DnsServerZoneAging -Name 'contoso.com' -ComputerName DC02 | Format-List *

# Read-only. Every zone's aging state on that server, including reverse zones (the loop is our form)
Get-DnsServerZone -ComputerName DC02 | Where-Object { -not $_.IsAutoCreated } | ForEach-Object {
  Get-DnsServerZoneAging -Name $_.ZoneName -ComputerName DC02 -ErrorAction SilentlyContinue
} | Format-Table -AutoSize
</code></pre>
<pre><code class="language-console">:: Read-only. The zone's registry view: aging, refresh / no refresh (hours), scavenge available, and the Scavenge Servers list
dnscmd DC02 /zoneinfo contoso.com
</code></pre>
<p>Microsoft's article on records that never get deleted (KB 2791165) uses exactly that <code>dnscmd /zoneinfo</code> output. Its sample shows <code>aging = 1</code>, <code>refresh interval = 168</code>, <code>no refresh = 168</code>, and a <strong>Scavenge Servers</strong> entry with one address. Its explanation of that address: "It's the IP address of the server that has the permission to scavenge the zone (Most likely this could be a server that is no longer existing)". The cause it gives: the zone "was previously configured manually for scavenging by a different server (using dnscmd), then scavenging was disabled in the environment and then enabled again on another server." <code>Set-DnsServerZoneAging</code> describes the default when that list is empty: "If you do not specify any scavenge servers, any primary DNS server that is authoritative for the zone can scavenge."</p>
<p><strong>The zone's own waiting period.</strong> When aging is turned on, the zone isn't eligible right away. Microsoft: "When you first enable aging on a zone, the zone gets one refresh interval of protection from scavenging." The DNS console shows it as <strong>The zone can be scavenged after</strong> in the zone's <strong>Aging</strong> dialog; Microsoft's note if it's missing: "If you don't see The zone can be scavenged after timestamp, reload the zone." The clock also resets. Microsoft's list of events that reset it includes "You enable dynamic updates for the zone." and "The DNS Server service starts and loads a scavenging-enabled primary zone." So a DNS server that restarts often, or a zone whose settings someone keeps toggling, can keep pushing the first cleanup out (our reading).</p>
<p><strong>Why a zone gets skipped.</strong> Microsoft's list of reasons a scavenging pass skips a zone:</p>
<table>
<thead>
<tr>
<th>Microsoft's skip reason</th>
<th>Read-only check</th>
</tr>
</thead>
<tbody><tr>
<td>"The server isn't an authorized scavenging server for the zone."</td>
<td>Scavenge Servers in <code>dnscmd /zoneinfo</code>; the scavenge-servers audit event (Step 5)</td>
</tr>
<tr>
<td>"Aging is disabled on the zone."</td>
<td><code>Get-DnsServerZoneAging</code> (<code>AgingEnabled</code> is the alias Microsoft lists for <code>-Aging</code>)</td>
</tr>
<tr>
<td>"The current server time is earlier than the zone's available for scavenging time."</td>
<td><code>scavenge available</code> in <code>/zoneinfo</code>, or the console's "can be scavenged after"</td>
</tr>
<tr>
<td>"The zone is paused, a cache zone, or an offline-signed zone."</td>
<td><code>Get-DnsServerZone</code> output</td>
</tr>
<tr>
<td>"The zone's Active Directory directory partition hasn't replicated since the DNS server started."</td>
<td>Step 7</td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Aging off on the zone (or only on the forward zone)</td>
<td>Scavenging skips the zone. Reverse zones are separate zones with their own aging (our reading)</td>
<td>Step 9: enable aging on that zone</td>
</tr>
<tr>
<td>"Set Aging/Scavenging for All Zones" used without the checkbox</td>
<td>"Without this option, new settings apply only to zones created afterward."</td>
<td>Step 9</td>
</tr>
<tr>
<td>Scavenge Servers lists an address that isn't this server</td>
<td>Only that server may scavenge; if it's gone, nobody does</td>
<td>Step 9: <code>/zoneresetscavengeservers</code></td>
</tr>
<tr>
<td>The zone's "can be scavenged after" time is in the future</td>
<td>Inside the first refresh interval, or reset by a restart</td>
<td>Wait, then Step 5</td>
</tr>
<tr>
<td>Secondary or stub zone</td>
<td>Not supported; scavenge on the primary or AD-integrated copy</td>
<td>The zone's primary server</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Do the stale records have a timestamp? (read-only)</h2>
<p>Microsoft: "The timestamp of static records is 0, indicating they aren't scavenged." That covers every record someone created by hand in the console or with <code>Add-DnsServerResourceRecordA</code> without <code>-AgeRecord</code>. Microsoft's dynamic update troubleshooting guide adds a less obvious group: "If you recently enabled the given option, the records that were registered before the change won't carry a timestamp or be scavenged." And the <code>dnscmd</code> page lists record types that never take part: "Name server (NS) resource records, start of authority (SOA) resource records, and Windows Internet Name Service (WINS) resource records aren't included in the scavenging process".</p>
<p>Read the records you wrote down in Step 1, then count static versus timestamped records in the zone:</p>
<pre><code class="language-powershell"># Read-only. The Step 1 records with their timestamps (static records show a zero or empty Timestamp; the RecordData.IPv4Address filter is our form)
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -Name 'FIN-LT-114' -ComputerName DC02
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'A' -ComputerName DC02 |
  Where-Object { $_.RecordData.IPv4Address -eq '10.20.30.41' } | Select-Object HostName, Timestamp, TimeToLive

# Read-only. How many A records are static vs timestamped (the grouping is our form)
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'A' -ComputerName DC02 |
  Group-Object { if ($_.Timestamp) { 'Timestamped' } else { 'Static (no timestamp)' } } | Select-Object Name, Count
</code></pre>
<p>Then list the records that are already past both intervals, which is what Microsoft's setup guide tells you to look at before any cleanup ("Look for any records older than the Refresh plus No-Refresh interval in your DNS records."):</p>
<pre><code class="language-powershell"># Read-only. Timestamped A records older than no-refresh + refresh (interval property names follow the -NoRefreshInterval / -RefreshInterval parameters; the filter is our form)
$aging  = Get-DnsServerZoneAging -Name 'contoso.com' -ComputerName DC02
$cutoff = (Get-Date) - ($aging.NoRefreshInterval + $aging.RefreshInterval)
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'A' -ComputerName DC02 |
  Where-Object { $_.Timestamp -and $_.Timestamp -lt $cutoff } |
  Select-Object HostName, Timestamp, @{ n = 'IPv4'; e = { $_.RecordData.IPv4Address } } | Sort-Object Timestamp
</code></pre>
<p>Run it on the scavenging server. Timestamps only replicate once aging is on. A community-written TechNet Wiki article archived on Microsoft Learn puts it this way: "when DNS aging and scavenging is disabled on an AD-integrated DNS zone, the timestamps of resource records on your DC/DNS servers are not consistent". Microsoft's setup article says the same in its own words.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Stale record shows timestamp 0</td>
<td>Static: never scavenged</td>
<td>Delete it deliberately, or stamp it (Step 9)</td>
</tr>
<tr>
<td>Many old records are static, created before aging was on</td>
<td>Microsoft's "registered before the change" note</td>
<td>Step 9: one node or one record at a time, not <code>/ageallrecords</code></td>
</tr>
<tr>
<td>Record past both intervals, nonzero timestamp</td>
<td>It should go on the next eligible pass</td>
<td>Step 5: did a pass run on an authorized server?</td>
</tr>
<tr>
<td>Record has a recent timestamp but the machine is gone</td>
<td>Something is still refreshing it</td>
<td>Step 6</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: What do the scavenging events say? (read-only)</h2>
<p>After each pass the scavenging server logs one of two events in its <strong>DNS Server</strong> log. Event 2501's message starts "The DNS server has completed a scavenging cycle" and lists visited zones, visited nodes, scavenged nodes, and scavenged records, then "The next scavenging cycle is scheduled to run in %6 hours." Event 2502 reads "The DNS server has completed a scavenging cycle but no nodes were visited." Its listed causes start with "1) No zones are configured for scavenging by this server." Microsoft's newer configuration page calls 2502 "the normal, healthy state when all records are current", so read it with Step 3's findings next to it (our reading).</p>
<p>Microsoft's configuration page adds three AD-integrated zone events that block scavenging: 4013 (waiting for AD DS), 4515 (duplicate zone in AD), and 4521 (zone couldn't load). The <strong>Audit</strong> channel (on by default, per Microsoft: "DNS audit logs are enabled by default") shows the detail: 521 for each scavenged record, 552 for a cycle start, 554 for a cycle that was terminated, and 567 for a change to the scavenge servers.</p>
<pre><code class="language-powershell"># Read-only. The DNS log names on this server (list first; the exact names below are our form)
Get-WinEvent -ListLog '*DNS*' -ErrorAction SilentlyContinue | Select-Object LogName, RecordCount, IsEnabled

# Read-only. On the scavenging server: last 30 days of scavenging and zone-load events in the DNS Server log
Get-WinEvent -FilterHashtable @{ LogName = 'DNS Server'; Id = 2501, 2502, 4013, 4515, 4521; StartTime = (Get-Date).AddDays(-30) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List

# Read-only. The audit view: records scavenged (521), cycle start / termination (552 / 554), scavenge servers (567)
Get-WinEvent -FilterHashtable @{ LogName = 'Microsoft-Windows-DNSServer/Audit'; Id = 521, 552, 554, 567; StartTime = (Get-Date).AddDays(-30) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<p>The 521 message is the one to search for a name you care about: "A resource record of type %1, name %2, TTL %3 and RDATA %5 was scavenged from scope %7 of zone %6." The 567 message names who may scavenge: "Only %2 can run scavenging." Microsoft's note on the audit log: it's "local to the given DNS server", so read it on the server that scavenged.</p>
<p><strong>When will the next pass run?</strong> Microsoft's setup article: "By getting the timestamp on the most recent Event ID 2501 or 2502 and adding the scavenging period to it, you can tell exactly when a server will attempt to scavenge." Its newer page softens that: it "gives an estimate, but the event time reflects completion of the pass rather than its start time." And the period restarts with the service: "It resets whenever the DNS service restarts."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No 2501 / 2502 on any server</td>
<td>No server scavenging, or the zone isn't available (Microsoft's replication scenario)</td>
<td>Steps 2, 7</td>
</tr>
<tr>
<td>2502 on the configured server, every cycle</td>
<td>No zone this server may scavenge, or nothing eligible yet</td>
<td>Step 3</td>
</tr>
<tr>
<td>2501 with <code>Scavenged Records</code> above 0, Step 1 records survive</td>
<td>Those records aren't eligible: no timestamp, or still refreshing</td>
<td>Steps 4, 6</td>
</tr>
<tr>
<td>4013 / 4515 / 4521</td>
<td>Zone loading or AD problems block scavenging</td>
<td>Fix the zone load first; <a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> for the replication side</td>
</tr>
<tr>
<td>521 names a record you needed</td>
<td>Over-deletion</td>
<td>Step 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Is something still refreshing the "stale" record? (read-only)</h2>
<p>A record that keeps a fresh timestamp isn't stale, whatever the CMDB says. Microsoft's refresh rules: "Windows clients dynamically update DNS every 24 hours." For statically addressed machines: "Clients also update every 24 hours to refresh records in the DNS database." So a laptop that's still on the VPN, a reimaged machine that kept its name, or a second adapter registering an extra address all keep their records alive (our reading of those rules).</p>
<p><strong>Who owns the record.</strong> In a secure-only AD-integrated zone, ownership decides who can update or delete a record. Microsoft: "When a DHCP server performs a secure dynamic update on a name, it becomes the owner of that name. Only that DHCP server can update any record for that name." Its older DnsUpdateProxy article describes how that leaves stale records behind: "The DNS server can end up with stale resource records when a DHCP server is configured to dynamically register host (A) and pointer (PTR) resource records on behalf of DHCP clients using dynamic update." Microsoft's setup article puts the owner check in its sanity phase: "Who is the owner of the record (see the Security tab in the record properties)?" The same check from PowerShell:</p>
<pre><code class="language-powershell"># Read-only. Owner and tombstone state of one dnsNode in an AD-integrated zone (zone DN format from Microsoft's /zoneinfo sample; the AD: drive form is ours)
Import-Module ActiveDirectory
$zoneDN = 'DC=contoso.com,CN=MicrosoftDNS,DC=DomainDnsZones,DC=contoso,DC=com'
$node   = "DC=FIN-LT-114,$zoneDN"
(Get-Acl -Path "AD:\$node").Owner
Get-ADObject -Identity $node -Properties dNSTombstoned, whenChanged | Select-Object Name, dNSTombstoned, whenChanged
</code></pre>
<p><strong>What DHCP is doing.</strong> If DHCP registers records for clients, read its DNS settings for the scope. Microsoft on <code>Get-DhcpServerv4DnsSetting</code>: "This cmdlet gets the effective DNS setting at the specified level such as reservation, scope, or server."</p>
<pre><code class="language-powershell"># Read-only. DHCP's DNS update settings for one scope, its leases, and the DNS update account (server and scope are examples)
Get-DhcpServerv4DnsSetting -ComputerName 'dhcp01.contoso.com' -ScopeId 10.20.30.0
Get-DhcpServerv4Scope -ComputerName 'dhcp01.contoso.com' -ScopeId 10.20.30.0 | Format-List *
Get-DhcpServerv4Lease -ComputerName 'dhcp01.contoso.com' -ScopeId 10.20.30.0 -AllLeases |
  Where-Object IPAddress -eq '10.20.30.41'
Get-DhcpServerDnsCredential -ComputerName 'dhcp01.contoso.com'
</code></pre>
<p>The fields to read mirror the <code>Set-DhcpServerv4DnsSetting</code> parameters (our reading): <code>DynamicUpdates</code> ("Always", "Never", or "OnClientRequest"), <code>DeleteDnsRROnLeaseExpiry</code> ("Specifies that the DHCP server service should delete the DNS resource records for the client after the lease expires."), and <code>NameProtection</code>. The scope's lease duration is in the <code>Format-List *</code> output.</p>
<p><strong>The lease and the intervals.</strong> Microsoft's DHCP lease blog walks through the classic failure: an 8-day lease, 7 + 7 day intervals, the lease expires, the address goes to a new client, and the old record stays: "Client-A is the owner of its DNS record so it cannot be deleted by the DHCP server (by default)." Now two names resolve to one address, and Kerberos to the wrong machine fails, which is the Helpdesk ticket above. Microsoft's two current pages don't agree on which way to size this. The scavenging troubleshooting article: "Refresh + No-refresh &gt;= The maximum DHCP lease". The configuration article's common mistakes: "Align the combined No-refresh and Refresh intervals with your DHCP lease duration so that the combined interval is equal to or less than the lease." Our reading: "greater than or equal" protects live clients from deletion, and "equal to or less" clears reused addresses sooner. The blog lists both options with their trade-offs. Pick one on purpose.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Timestamp keeps moving on a "retired" name</td>
<td>Something still registers it: a live device, a second NIC, or a reimaged machine with the same name (our reading)</td>
<td>Find the device, not the record</td>
</tr>
<tr>
<td>Owner is a DHCP server's computer account</td>
<td>Only that DHCP server can update or delete it</td>
<td>Step 9: DHCP credentials, lease expiry deletion, or let scavenging remove it</td>
</tr>
<tr>
<td><code>DeleteDnsRROnLeaseExpiry</code> false where DHCP registers records</td>
<td>Expired leases leave their records behind (our reading)</td>
<td>Step 9</td>
</tr>
<tr>
<td>Two names on one IP, lease shorter than both intervals</td>
<td>Microsoft's lease blog scenario</td>
<td>Step 9: intervals vs lease, chosen deliberately</td>
</tr>
<tr>
<td>Machine in Azure</td>
<td>"Azure machines have a lease of 136 years." Their DHCP refresh never comes</td>
<td>Microsoft's <strong>Registration Refresh Interval</strong> policy (Step 8)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: AD-integrated zones: replication and which server's copy (read-only)</h2>
<p>In an AD-integrated zone, deletions travel by AD replication, and Microsoft protects against a server that's been out of touch. From its troubleshooting article, for the case where scavenging doesn't occur and events 2501 and 2502 aren't generated: "verify that the zone is available for scavenging by ensuring that the partition (where the zone is stored) has replicated since the system boot." Its rule: "If the partition hasn't fully synced with at least one replication partner in the last zone refresh interval, don't scavenge the zone." The scenario it describes is a DC whose replication partner is being decommissioned. Its fix, if that DC isn't needed: remove it.</p>
<p>Microsoft also says replication isn't a substitute for scavenging: "Active Directory replication alone doesn't remove stale records, so you still need these features on integrated zones."</p>
<p>Compare what each DNS server answers and check replication, using the reads Microsoft's configuration page names:</p>
<pre><code class="language-powershell"># Read-only. Ask each DNS server directly for the same name, DNS only (server names are examples; the loop is our form)
foreach ($dns in 'DC01', 'DC02', 'DC03') {
  Resolve-DnsName -Name 'FIN-LT-114.contoso.com' -Server $dns -DnsOnly -ErrorAction SilentlyContinue |
    Select-Object @{ n = 'Server'; e = { $dns } }, Name, Type, IPAddress
}
</code></pre>
<pre><code class="language-console">:: Read-only. Replication health, then one DC's inbound partners (Microsoft's configuration page names both commands; the DC argument is our form)
repadmin /replsummary
repadmin /showrepl DC02
</code></pre>
<p>Microsoft's caution when servers seem to disagree: "Cached responses can persist in a client's cache or in a non-authoritative server's cache after replication finishes, so a stale cached response doesn't necessarily indicate a replication failure." Query the authoritative servers, not a client (our reading of that note).</p>
<p>A scavenged record isn't removed from AD at once. The TechNet Wiki article: "the resource record dNSTombstoned attribute will be set to TRUE on its AD object when it is scavenged." Step 6's <code>Get-ADObject</code> read shows it.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Replication failures on the scavenging DC</td>
<td>The zone isn't available to scavenge, by design</td>
<td><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a>, then Step 5 again</td>
</tr>
<tr>
<td>A decommissioned DC still listed</td>
<td>Microsoft's scenario for missing 2501/2502</td>
<td>Finish the decommission; <code>nltest /dclist:</code> to confirm</td>
</tr>
<tr>
<td>Servers answer differently, replication healthy</td>
<td>Caching, or the record is in a different partition or zone (our reading)</td>
<td>Check the zone's replication scope in <code>Get-DnsServerZone</code></td>
</tr>
<tr>
<td><code>dNSTombstoned</code> true on a record that still resolves somewhere</td>
<td>A server or cache is behind (our reading)</td>
<td>Replication, then client cache</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: The opposite problem: scavenging deleting live records (read-only)</h2>
<p>Microsoft's DNS troubleshooting guide: "If DNS records go missing from DNS zones, scavenging is the most common cause." Its example: "if these values are both less than 24 hours, you lose DNS records." The scavenging article walks through the same case with 2-hour intervals and a server that refreshes every 24 hours: "The cause is that the Refresh + No-refresh interval is less than 24 hours." Microsoft's older aging guide gives the rule for the refresh interval: "it is important that the length of time used be greater than the maximum possible refresh period for any resource records that are contained in the zone." Its table puts the DHCP server's refresh at "Four days (half of the lease interval, which is eight days by default)."</p>
<p>The other ways live records go, each from the Microsoft page named:</p>
<table>
<thead>
<tr>
<th>Cause</th>
<th>Microsoft's text</th>
<th>Read-only check</th>
</tr>
</thead>
<tbody><tr>
<td><code>dnscmd /ageallrecords</code> on the zone</td>
<td>It sets a timestamp "for all records in the zone, including static records that you don't want to be scavenged."</td>
<td>Static servers and printers with nonzero timestamps (Step 4)</td>
</tr>
<tr>
<td>A static record that was dynamic</td>
<td>"DDNS can cause a static record to be deleted from DNS if that record is registered as a dynamic record and then converted to a static record by removing its timestamp."</td>
<td>Who has delete rights on the record (Step 6 owner read)</td>
</tr>
<tr>
<td>Updates failing, so records age out</td>
<td>"The DNS Dynamic Update Protocol update to existing records fails. Because of this issue, the DNS scavenging process considers the records to be aged, and it deletes them."</td>
<td>Registration failure events in the System log on clients, DCs (Netlogon), and DHCP servers</td>
</tr>
<tr>
<td>DHCP on a DC without credentials</td>
<td>The guide: "This can cause issues like the unintended scavenging of static records."</td>
<td><code>Get-DhcpServerDnsCredential</code> on that DC (Step 6)</td>
</tr>
<tr>
<td>Azure VMs</td>
<td>Their 136-year lease means no DHCP refresh</td>
<td><strong>Computer Configuration\Administrative Templates\Network\DNS Client\Registration Refresh Interval</strong></td>
</tr>
</tbody></table>
<p>For the Azure case, Microsoft's fix is that policy, which "maps to the registry key" <code>HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows NT\DNSClient</code>, with the note "To make the changes to this value effective, you must restart Windows."</p>
<p>If the over-deletion is happening now, stop the scavenging server first (Step 9, interval 0), then get the records back by re-registering the clients or re-creating them from your export. Microsoft says the <code>Export-DnsServerZone</code> file is for troubleshooting and "is not in the same format as a file-backed zonefile", so treat it as a reference list, not a one-step restore (our reading). Find the cause after (our habit).</p>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong><code>dnscmd /ageallrecords</code> on the whole zone</strong></td>
<td>Stamps static records too, and "A time stamp added by using the ageallrecords command can't be reversed."</td>
<td>Stamp one node, or tick <strong>Delete this record when it becomes stale</strong> on one record (Step 9)</td>
</tr>
<tr>
<td><strong><code>Set-DnsServerResourceRecordAging -Recurse</code> on the zone</strong></td>
<td>Same effect from PowerShell: it "sets a time stamp to the current time for records" in the zone</td>
<td>Step 4's list first; stamp only what should age</td>
</tr>
<tr>
<td><strong>Scavenging on every DC</strong></td>
<td>Microsoft recommends one per zone; with many, deletions are harder to trace</td>
<td>One server without FSMO roles (Step 2)</td>
</tr>
<tr>
<td><strong>Intervals cut to hours</strong></td>
<td>Records for machines that refresh every 24 hours get deleted (Step 8)</td>
<td>Size intervals to the longest refresh period and the DHCP lease (Step 6)</td>
</tr>
<tr>
<td><strong>Expecting a manual scavenge to clean up today</strong></td>
<td>"Note that manual attempts won't bypass the safety valves."</td>
<td>Read "can be scavenged after" and the record timestamps first (Steps 3, 4)</td>
</tr>
<tr>
<td><strong>Bulk-deleting old records by hand</strong></td>
<td>Records for live clients come back, or don't, depending on ownership (our reading)</td>
<td>Microsoft's sanity check: "Don't proceed unless you can explain any outdated records."</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Change one link, then prove it (only after the read)</h2>
<p>Pick the one change Steps 1-8 point at. Save the current state first. Microsoft's best practices: "Back up your zone data. Export zone data before you enable aging or trigger scavenging."</p>
<pre><code class="language-powershell"># Low-risk write. Export the zone and save the current settings (the file lands in C:\Windows\System32\dns by default; the redirections are our habit)
New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null
Export-DnsServerZone -Name 'contoso.com' -FileName 'contoso.com.before-scavenging.txt' -ComputerName DC02
Get-DnsServerScavenging -ComputerName DC02 | Format-List * &gt; C:\Temp\scavenging-before.txt
Get-DnsServerZoneAging -Name 'contoso.com' -ComputerName DC02 | Format-List * &gt; C:\Temp\zoneaging-before.txt
</code></pre>
<p><strong>Server scavenging on one server (Step 2).</strong> Turn it on where you want it and off where you don't. The interval form is Microsoft's (<code>dd.hh:mm:ss</code>; 0 disables):</p>
<pre><code class="language-powershell"># Change. Dry-run first: scavenging on DC02 every 7 days, off on DC01 (server names are examples)
Set-DnsServerScavenging -ComputerName DC02 -ScavengingState $true -ScavengingInterval 7.00:00:00 -WhatIf
Set-DnsServerScavenging -ComputerName DC01 -ScavengingInterval 0.00:00:00 -WhatIf
</code></pre>
<p>Leave <code>-ApplyOnAllZones</code> off unless you mean it. Microsoft: it's "the PowerShell equivalent of the DNS Manager Apply these settings to the existing Active Directory-integrated zones option".</p>
<p><strong>Zone aging and who may scavenge (Step 3).</strong> Microsoft's form, with the scavenging server's address (an example):</p>
<pre><code class="language-powershell"># Change. Dry-run first: aging on for the zone with 7 + 7 day intervals, and only 192.0.2.12 (DC02) may scavenge it
Set-DnsServerZoneAging -Name 'contoso.com' -ComputerName DC02 -Aging $true -NoRefreshInterval 7.00:00:00 -RefreshInterval 7.00:00:00 -ScavengeServers 192.0.2.12 -WhatIf
</code></pre>
<p>If <code>/zoneinfo</code> named a dead server, Microsoft's KB 2791165 fix is <code>dnscmd</code>. Its other form, without an address, "resets all scavenging server settings and removes manually specified servers":</p>
<pre><code class="language-console">:: Change. Allow only DC02 (192.0.2.12) to scavenge contoso.com, then read it back
dnscmd DC02 /zoneresetscavengeservers contoso.com 192.0.2.12
dnscmd DC02 /zoneinfo contoso.com
</code></pre>
<p>Remember the clock: changing aging resets the zone's available-for-scavenging time, so the first pass after this change is at least one refresh interval away (Step 3).</p>
<p><strong>Records without a timestamp (Step 4).</strong> For one record: open it with <strong>View &gt; Advanced</strong> on, tick <strong>Delete this record when it becomes stale</strong>, and apply. Microsoft: "the current time is rounded down to the nearest hour and applied as the timestamp on the record." For one name with all its records, <code>Set-DnsServerResourceRecordAging -NodeName</code>. Microsoft notes it "stamps every record at a node" and says "Don't use it to age a single record." For an old record an admin created by hand, Microsoft's sanity-check advice: "you need to delete the record to clear the ownership and run the ipconfig /registerdns command to update it." For a record that should just go:</p>
<pre><code class="language-powershell"># Change. Dry-run first: remove one stale A record by name and address (Microsoft's example form; names are examples)
Remove-DnsServerResourceRecord -ZoneName 'contoso.com' -RRType 'A' -Name 'OLD-LT-022' -RecordData '10.20.30.41' -ComputerName DC02 -WhatIf
</code></pre>
<p>To keep a critical record from ever being scavenged, Microsoft's note: "No single cmdlet clears an existing record's timestamp." Its method is to remove the record and add it back without <code>-AgeRecord</code>, with its caution that this "changes the record's ownership and permissions and can affect later dynamic registration by the client."</p>
<p><strong>DHCP (Step 6).</strong> Where DHCP registers records, make it clean up after expired leases. A dry run first:</p>
<pre><code class="language-powershell"># Change. Dry-run first: delete DNS records when the lease expires, for one scope (DynamicUpdates must be Always or OnClientRequest, per Microsoft)
Set-DhcpServerv4DnsSetting -ComputerName 'dhcp01.contoso.com' -ScopeId 10.20.30.0 -DeleteDnsRROnLeaseExpiry $true -WhatIf
</code></pre>
<p>If the DHCP server runs on a DC or several DHCP servers share clients, Microsoft's fix is a dedicated account for DNS updates (DHCP console: <strong>IPv4</strong> &gt; <strong>Properties</strong> &gt; <strong>Advanced</strong> &gt; <strong>Credentials</strong>), not the DnsUpdateProxy group alone: "When the DHCP server is a member of the DnsUpdateProxy group, it doesn't secure the DNS domain names it registers." Microsoft's dynamic update guide goes further and recommends letting clients register their own records: "the recommendation is not to use DHCP option 81 and instead let the client register its records." That's a design change; plan it with the network team (our view).</p>
<p><strong>Intervals vs the lease (Steps 6, 8).</strong> Change the zone intervals or the scope lease, not both on the same day, and pick the direction from Step 6 on purpose. Keep refresh + no-refresh at least 24 hours in every case (Microsoft's Step 8 cases).</p>
<p><strong>Then prove it.</strong> Microsoft's enable phase uses a test record: "create a new test record and enable it for scavenging. Then, map out the point in time when this record disappears."</p>
<pre><code class="language-powershell"># Change (one test record). Dry-run first; drop -WhatIf to create it. -AgeRecord gives it a timestamp
Add-DnsServerResourceRecordA -ZoneName 'contoso.com' -Name 'scavenge-test-01' -IPv4Address '192.0.2.250' -AgeRecord -ComputerName DC02 -WhatIf

# Read-only. Its timestamp, then the predicted deletion: timestamp + no-refresh + refresh, then the next pass after that (Step 5)
Get-DnsServerResourceRecord -ZoneName 'contoso.com' -Name 'scavenge-test-01' -ComputerName DC02 | Select-Object HostName, Timestamp
</code></pre>
<p>Microsoft's worked example: with 3-day intervals, a 3-day scavenging period, a last 2501/2502 at 6 AM on 1/1/2008, and a record stamped at noon that day, "you can predict that the record will be deleted at approximately 6 am on 1/10/2008." When your predicted time passes, confirm with the 2501 on DC02 and the 521 audit event naming <code>scavenge-test-01</code>, then check one Step 1 record the same way.</p>
<p>A manual pass is fine for a test once everything is eligible. Microsoft on <code>Start-DnsServerScavenging</code>: "If the DNS server cannot run scavenging, the cmdlet displays the reasons why the scavenge did not start." And from the troubleshooting article: "This will result in the instant deletion of records only if scavenging has been run automatically at least once on this server."</p>
<pre><code class="language-powershell"># Change (deletes eligible stale records). Dry-run first; -Verbose lists what it deletes (Microsoft's example)
Start-DnsServerScavenging -ComputerName DC02 -Verbose -WhatIf
</code></pre>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Nobody scavenges</td>
<td>One server, without FSMO roles, 7-day period</td>
<td>Scavenging on every DC</td>
</tr>
<tr>
<td>Scavenge Servers names a dead DC</td>
<td><code>dnscmd /zoneresetscavengeservers</code> with the live server</td>
<td>Waiting for more cycles</td>
</tr>
<tr>
<td>Old records are static</td>
<td>One record or one node stamped, or deleted after a check</td>
<td><code>dnscmd /ageallrecords</code> on the zone</td>
</tr>
<tr>
<td>DHCP owns stale records</td>
<td>Lease-expiry deletion, a dedicated DNS update account</td>
<td>DnsUpdateProxy alone in a secure-only zone</td>
</tr>
<tr>
<td>Live records being deleted</td>
<td>Interval 0 on the scavenging server, then longer intervals</td>
<td>Turning aging off and on repeatedly (it resets the clock)</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why is DNS scavenging not working?</strong>
Usually one of the three levels is missing. The server's scavenging interval is 0, aging is off for that zone, the zone only lets a different (often retired) server scavenge it, or the stale records have no timestamp. Event 2502 "no nodes were visited" on the server you configured points at the zone side first.</p>
<p><strong>How long does DNS scavenging take to delete a stale record?</strong>
With defaults, a record becomes stale after 14 days without a refresh, and the next pass deletes it, so Microsoft says up to 21 days. A zone that just had aging turned on also waits one refresh interval before it can be scavenged at all.</p>
<p><strong>Why are static DNS records not scavenged?</strong>
Their timestamp is 0, and scavenging only considers records with a nonzero timestamp. That's by design for servers and printers. To make one age, tick <strong>Delete this record when it becomes stale</strong> on that record. Avoid stamping the whole zone.</p>
<p><strong>What are DNS events 2501 and 2502?</strong>
Both are logged by the scavenging server after a cycle. 2501 reports what it visited and scavenged and when the next cycle runs. 2502 means it completed a cycle with no nodes visited, and Microsoft's first listed cause is that no zones are configured for scavenging by that server.</p>
<p><strong>What should the DNS refresh and no-refresh intervals be with DHCP?</strong>
Microsoft's pages don't agree on direction: the scavenging troubleshooting article says refresh + no-refresh should be at least the maximum DHCP lease, and the configuration article says equal to or less than the lease. Neither should be below 24 hours. Decide whether deleting a live record or keeping a reused address is the bigger risk in that subnet.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage-1j65">Active Directory Replication Error 1722</a> (a stale DC A record, and the replication health scavenging depends on)</li>
<li><a href="https://dev.to/adminpackstudio/windows-server-dhcp-not-assigning-ip-addresses-a-read-first-scope-authorization-relay-and-d13">Windows Server DHCP Not Assigning IP Addresses</a> (scopes, leases, and failover on the DHCP side of the same records)</li>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (when a client finds the wrong DC through DNS)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (staleness is measured against the DNS server's clock, so a wrong clock skews it; our reading)</li>
</ul>
<h2>Free checklist</h2>
<p>Turning on scavenging on every DC with one-hour intervals before one test record was watched through a full cycle is a change nobody tested where the deletions happen. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dns-scavenging-not-working">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dns-scavenging-not-working</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dns-scavenging-not-working">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dns-scavenging-not-working</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 favor read-only checks: <code>nltest /dclist</code>, <code>dnscmd /info</code>, <code>dnscmd /zoneinfo</code>, <code>Get-DnsServerScavenging</code>, <code>Get-DnsServerZone</code>, <code>Get-DnsServerZoneAging</code>, <code>Get-DnsServerResourceRecord</code>, <code>Get-WinEvent</code>, <code>Get-Acl</code>, <code>Get-ADObject</code>, <code>Get-DhcpServerv4DnsSetting</code>, <code>Get-DhcpServerv4Scope</code>, <code>Get-DhcpServerv4Lease</code>, <code>Get-DhcpServerDnsCredential</code>, <code>Resolve-DnsName</code>, and <code>repadmin</code>. <code>Export-DnsServerZone</code> and output redirection write local files. <code>Set-DnsServerScavenging</code>, <code>Set-DnsServerZoneAging</code>, <code>dnscmd /zoneresetscavengeservers</code>, record timestamps, <code>Remove-DnsServerResourceRecord</code>, <code>Set-DhcpServerv4DnsSetting</code>, DHCP credentials, interval and lease changes, the test record, and <code>Start-DnsServerScavenging</code> are deliberate changes. Change one link at a time, prove it with one test record first, and check current Microsoft Learn for version-specific behavior.</em></p>
]]></content:encoded></item><item><title><![CDATA[Certificate Autoenrollment Not Working: A Read-First GPO, Template Permission, CA, and DCOM Triage]]></title><description><![CDATA[Certificate autoenrollment not working? Read the applied policy, Event 6, template Autoenroll rights, the CA's templates, and DCOM first.]]></description><link>https://adminpackstudio.hashnode.dev/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/certificate-autoenrollment-not-working-a-read-first-gpo-template-permission-ca-and-dcom-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 14:41:58 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "The new 802.1X Wi-Fi needs a computer certificate on every laptop. About half of the Finance laptops got one this week; the rest show nothing in the Personal store. The PKI admin duplicated the Workstation Authentication template on Monday and says the CA is up. Event Viewer on one laptop says automatic certificate enrollment for local system failed, the RPC server is unavailable. A blog post says to give Domain Computers Full Control on the template, add Authenticated Users to the CA's local Administrators group, and run certreq on every laptop. Can we start with the permissions?"</p>
<p>Not yet. <strong>Autoenrollment isn't one setting. It's a chain that runs on the client: policy says enroll, the client finds a template it's allowed to autoenroll, finds an enterprise CA that publishes that template, reaches the CA over RPC/DCOM, and the CA issues instead of denying or pending the request.</strong> Microsoft on when it runs: "Windows triggers automatic certificate enrollment for the computer during boot, and when Group Policy updates." And on what the identity needs: "User or computer has Read, Enroll, and Autoenroll permissions on the certificate template being requested." Full Control on a template and local admin on the CA skip past the link that failed and leave a wider permission behind. Work through it as a chain: <strong>which identity (user or computer), which template, since when</strong> → <strong>is autoenrollment policy actually applied on the client?</strong> → <strong>the CertificateServicesClient events and their error codes</strong> → <strong>the template: version, Read / Enroll / Autoenroll for that identity, and its flags</strong> → <strong>is the template published on an enterprise CA the client can see?</strong> → <strong>can the client reach the CA: DC, port 135, the RPC range, CertSvc?</strong> → <strong>DCOM and access on the CA (0x800706ba that's really access denied)</strong> → <strong>did the request reach the CA and get denied or left pending?</strong> → <strong>chain, revocation, and renewal timing</strong> → <strong>domain controller certificates, which follow their own rules</strong>. <code>gpresult</code>, <code>Get-CertificateAutoEnrollmentPolicy</code>, <code>Get-WinEvent</code>, <code>certutil</code>, <code>Get-ADObject</code>, <code>Get-Acl</code>, and <code>Get-LocalGroupMember</code> are the sources for that chain. Run <code>certreq</code> on every laptop with the same broken link, and every laptop fails the same way.</p>
<p>If the devices are Intune-managed and get certificates through a SCEP profile and NDES instead of Group Policy, read <a href="https://dev.to/adminpackstudio/intune-scep-certificate-profile-failed-a-read-first-assignment-and-ndes-triage-4ibc">Intune SCEP Certificate Profile Failed</a> first. That's a different enrollment path. If no Group Policy is landing on the client at all, start with <a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a>. This post covers <strong>"Domain-joined users or computers don't get (or don't renew) a certificate from an AD CS enterprise CA through Group Policy autoenrollment, and you need a read-first path before you widen template permissions, change DCOM on the CA, or enroll by hand."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Prove the policy landed first.</strong> <code>Get-CertificateAutoEnrollmentPolicy -Scope Applied</code> and the <code>AEPolicy</code> registry value show whether autoenrollment is on for that context (Step 2).</li>
<li><strong>Read the AutoEnrollment events before you change anything.</strong> Microsoft: "By default, autoenrollment logs errors/failures and successful enrollments in the Application event log on the client machine." The error code in the event names the link: 0x8007054b is getting to a DC, 0x800706ba is getting to the CA, 0x80094800 is the template on the CA.</li>
<li><strong>The template needs three rights, and the CA needs one.</strong> The identity needs Read, Enroll, and Autoenroll. The CA's computer account needs Read: if Authenticated Users is removed, Microsoft says "the (enterprise) CA itself can no longer read the template in the Active Directory."</li>
<li><strong>A template does nothing until a CA publishes it.</strong> Microsoft: superseding "isn't active until the template is published to one or more certificate authorities." <code>certutil -TemplateCAs</code> lists which CAs offer it.</li>
<li><strong>0x800706ba isn't always the network.</strong> Microsoft's article for it lists six causes, and most are access on the CA: the <code>Certificate Service DCOM Access</code> group, network logon rights, <code>EnableDCOM</code>, and the COM Security limits (Step 7).</li>
<li><strong>Domain controllers are a special case.</strong> They request the old Domain Controller template on their own; newer templates need an autoenrollment GPO on the Domain Controllers OU (Step 10).</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which identity, which template, which machines, since when, what changed</td>
<td>"Certificates aren't working" with no template name</td>
</tr>
<tr>
<td>2. Policy applied</td>
<td>Autoenrollment enabled in the applied policy for that context (user or computer)</td>
<td><code>AEPolicy</code> missing; GPO linked to the wrong OU or the wrong half (User vs Computer)</td>
</tr>
<tr>
<td>3. Events</td>
<td>AutoEnrollment events read, error code noted</td>
<td>Event 6 or 13 nobody has decoded</td>
</tr>
<tr>
<td>4. Template</td>
<td>Version 2 or later for user autoenrollment; Read, Enroll, Autoenroll for this identity; CA can read it</td>
<td>Autoenroll granted to a group the computer isn't in; Authenticated Users removed</td>
</tr>
<tr>
<td>5. Published</td>
<td>An enterprise CA lists the template under Certificate Templates</td>
<td>Template duplicated but never issued</td>
</tr>
<tr>
<td>6. CA reachable</td>
<td>Client reaches a DC and the CA over 135 and the dynamic range; CertSvc running</td>
<td>Event 15 (no DC); 0x800706ba across a firewall or NAT</td>
</tr>
<tr>
<td>7. DCOM and access on the CA</td>
<td>The identity can log on to the CA over the network and activate its DCOM interface</td>
<td>0x800706ba with DCOM 10027 on the CA</td>
</tr>
<tr>
<td>8. Request on the CA</td>
<td>Issued, not denied or pending</td>
<td>Pending forever (manager approval); denied 0x80094800</td>
</tr>
<tr>
<td>9. Chain and renewal</td>
<td>CA chain and CRLs valid; renewal window sane</td>
<td>Revocation server offline; Event 64; renews too often</td>
</tr>
<tr>
<td>10. Domain controllers</td>
<td>A DC autoenrollment GPO and a current Kerberos Authentication-based template</td>
<td>DCs still on the old Domain Controller certificate</td>
</tr>
<tr>
<td>11. Change</td>
<td>Only after Steps 1-10 agree: one link, one change</td>
<td>Full Control on the template, same failure</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>User or computer certificate?</strong></td>
<td>Different policy half, different registry hive, different event context ("local system" vs the user), different token (our reading)</td>
</tr>
<tr>
<td><strong>Template name</strong> (the CN, not only the display name)</td>
<td><code>certutil</code> and AD use the template name; the console shows the display name. They can differ</td>
</tr>
<tr>
<td><strong>Which machines or users fail, and which work</strong></td>
<td>A group, an OU, a site, or an OS version in common is the first lead</td>
</tr>
<tr>
<td><strong>New enrollment or renewal?</strong></td>
<td>Never had one: Steps 2-7. Had one and it didn't renew: Steps 8-9</td>
</tr>
<tr>
<td><strong>What changed?</strong></td>
<td>Template duplicated or edited, CA renewed or migrated, GPO moved, firewall change, DCOM hardening updates, a group removed from a template ACL</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>No AutoEnrollment events at all</td>
<td>Policy not applied, or autoenrollment disabled in the registry</td>
<td>Step 2</td>
</tr>
<tr>
<td>Event 15 with 0x8007054b</td>
<td>Microsoft: "is due to problems getting to a DC in the domain, common cause is name resolution."</td>
<td>Step 6</td>
</tr>
<tr>
<td>Event 6 or 13 with 0x800706ba</td>
<td>The CA isn't reachable over RPC, or DCOM on the CA refuses the caller</td>
<td>Steps 6, 7</td>
</tr>
<tr>
<td>0x80094800, "The requested certificate template is not supported by this CA."</td>
<td>The CA doesn't publish the template, or can't read it</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>User certificates work, computer certificates don't (or the reverse)</td>
<td>The other policy half, or the other identity's rights on the template</td>
<td>Steps 2, 4</td>
</tr>
<tr>
<td>DCs keep the old Domain Controller certificate</td>
<td>No DC autoenrollment GPO, or the new template isn't published</td>
<td>Step 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Is autoenrollment policy applied on the client? (read-only)</h2>
<p>The GPO setting is <strong>Certificate Services Client - Auto-Enrollment</strong>, under <strong>Public Key Policies</strong>, in either <strong>Computer Configuration</strong> (computer certificates) or <strong>User Configuration</strong> (user certificates). Microsoft's configuration sets <strong>Configuration Model</strong> to <strong>Enabled</strong> and selects both <strong>Renew expired certificates, update pending certificates, and remove revoked certificates</strong> and <strong>Update certificates that use certificate templates</strong>. Read what actually applied, on the failing client:</p>
<pre><code class="language-console">:: Read-only. Elevated, on the client: computer-side RSoP to a file (run without /scope computer, as the user, for the user side)
gpresult /scope computer /h C:\Temp\gpresult-computer.html
</code></pre>
<pre><code class="language-powershell"># Read-only. The applied autoenrollment policy for the computer (elevated) and for the signed-in user (not elevated)
Get-CertificateAutoEnrollmentPolicy -Scope Applied -Context Machine
Get-CertificateAutoEnrollmentPolicy -Scope Applied -Context User
</code></pre>
<p>Microsoft on <code>-Scope</code>: "If Applied scope is specified, then the currently applied policy which can be either the local policy or a domain policy, is returned." The fields to read are <code>PolicyState</code>, <code>EnableMyStoreManagement</code> (the renew / update pending / remove revoked box), and <code>EnableTemplateCheck</code> (the update-certificates-that-use-templates box).</p>
<p>Microsoft's lower-level check is the registry. Its troubleshooting page warns that RSoP isn't the last word: "Rsop results will only show what was pushed by GPOs, not what actually was applied." So read <code>AEPolicy</code> in all four places it names:</p>
<pre><code class="language-powershell"># Read-only. AEPolicy in the policy keys (GPO) and the non-policy keys (local), HKLM = computer, HKCU = current user
'HKLM:\SOFTWARE\Policies\Microsoft\Cryptography\AutoEnrollment',
'HKCU:\SOFTWARE\Policies\Microsoft\Cryptography\AutoEnrollment',
'HKLM:\SOFTWARE\Microsoft\Cryptography\AutoEnrollment',
'HKCU:\SOFTWARE\Microsoft\Cryptography\AutoEnrollment' | ForEach-Object {
  [pscustomobject]@{ Key = $_; AEPolicy = (Get-ItemProperty -Path $_ -ErrorAction SilentlyContinue).AEPolicy }
}
</code></pre>
<p>Microsoft's wiki: "If none of these keys have AEPolicy = 7, Autoenrollment is not turned on." Its older troubleshooting blog adds the other value worth knowing: <code>AEPolicy</code> set to <code>0x00008000</code> means autoenrollment is disabled outright (<code>0x00008000==AUTO_ENROLLMENT_DISABLE_ALL</code>). Check HKCU while signed in as the affected user, not as an admin in another session (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No <code>AEPolicy</code> anywhere, GPO in RSoP</td>
<td>The GPO setting is there but not enabled, or another GPO wins with Not Configured</td>
<td>The winning GPO in the <code>gpresult</code> report</td>
</tr>
<tr>
<td>GPO missing from RSoP</td>
<td>Link, security filtering, WMI filter, or the client isn't processing policy</td>
<td><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a></td>
</tr>
<tr>
<td><code>AEPolicy</code> = <code>0x8000</code></td>
<td>Autoenrollment disabled (Microsoft's blog)</td>
<td>Find what set it (a GPO set to Disabled, or a script)</td>
</tr>
<tr>
<td><code>AEPolicy</code> = 7 in the policy key</td>
<td>Policy is fine</td>
<td>Step 3</td>
</tr>
<tr>
<td>Policy only in the local (non-policy) key</td>
<td>Set by <code>Set-CertificateAutoEnrollmentPolicy</code> or by hand; the wiki says it means "AD GPOs are not working"</td>
<td>Step 2 again for the domain GPO</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Read the CertificateServicesClient events (read-only, then one logging switch)</h2>
<p>Autoenrollment writes to the <strong>Application</strong> log. Read every CertificateServicesClient provider at once, so you don't have to guess provider names:</p>
<pre><code class="language-powershell"># Read-only. Last 7 days of CertificateServicesClient events in the Application log (the provider-name filter is our form)
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
  Where-Object ProviderName -like '*CertificateServicesClient*' |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message | Format-List

# Read-only. The certificate lifecycle channels on this OS (names vary by version; list them first)
Get-WinEvent -ListLog '*CertificateServices*' -ErrorAction SilentlyContinue |
  Select-Object LogName, RecordCount, IsEnabled
</code></pre>
<p>The events and what they point at:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Source and text</th>
<th>Points at</th>
</tr>
</thead>
<tbody><tr>
<td>2 / 3</td>
<td>Microsoft: "Stop and Start messages are event IDs 2 and 3."</td>
<td>Autoenrollment ran. No 2/3 at all: Step 2</td>
</tr>
<tr>
<td>6 (AutoEnrollment, Error)</td>
<td>Example from a community comment on Microsoft's PKI blog: "Automatic certificate enrollment for local system failed (0x800706ba) The RPC server is unavailable."</td>
<td>The error code in parentheses (table below)</td>
</tr>
<tr>
<td>13 (CertEnroll)</td>
<td>Microsoft: "Autoenrollment 13 is further into the chain, where we actually can try enroll for a certificate but fails." It names the template and the CA</td>
<td>The template and the CA it tried (Steps 5-8)</td>
</tr>
<tr>
<td>15 (AutoEnrollment)</td>
<td>Microsoft: with 0x8007054b, "is due to problems getting to a DC in the domain, common cause is name resolution." With 0x80072751, it "is commonly due to the client being logged on with cached credentials"</td>
<td>Step 6: DC and name resolution first</td>
</tr>
<tr>
<td>64 (AutoEnrollment, Warning)</td>
<td>Community post on Microsoft Q&amp;A: "Certificate for local system with Thumbprint ... is about to expire or already expired."</td>
<td>Step 9: a certificate nobody is renewing</td>
</tr>
<tr>
<td>82 (Application)</td>
<td>Microsoft's 0x800706ba article: "Event ID 82 is logged in Application logs if auto-enrollment is failing with the same error."</td>
<td>Step 7</td>
</tr>
</tbody></table>
<p>Common codes in Event 6 and 13, each from the Microsoft page named:</p>
<table>
<thead>
<tr>
<th>Code</th>
<th>Microsoft's text</th>
<th>Link in the chain</th>
</tr>
</thead>
<tbody><tr>
<td>0x800706ba</td>
<td>"The RPC Server is unavailable" (Microsoft's enrollment article); its causes are mostly access on the CA</td>
<td>Steps 6, 7</td>
</tr>
<tr>
<td>0x80070005</td>
<td>"Access is denied", which Microsoft's blog says "occurs when attempting to bind to the certification authority to generate the certificate request"</td>
<td>Step 7</td>
</tr>
<tr>
<td>0x80094800</td>
<td>"The requested certificate template is not supported by this CA." (KB 283218)</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>CRL error</td>
<td>"The revocation function was unable to check revocation because the revocation server was offline." (Microsoft's autoenrollment blog)</td>
<td>Step 9</td>
</tr>
</tbody></table>
<p><strong>Turn up the logging while you test.</strong> By default only errors and successful enrollments are logged. Microsoft's two troubleshooting pages give the same switch: create a DWORD <code>AEEventLogLevel</code> set to 0 under <code>Software\Microsoft\Cryptography\AutoEnrollment</code>, in HKLM for the computer and HKCU for the user. That's one small registry write; the dry run first:</p>
<pre><code class="language-powershell"># Low-risk write. Verbose autoenrollment logging for the computer context (dry-run first; remove it after the test, Step 11)
New-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Cryptography\AutoEnrollment' -Name AEEventLogLevel -PropertyType DWord -Value 0 -WhatIf
</code></pre>
<p>Microsoft's wiki on what a healthy run looks like after <code>gpupdate /force</code>: "Two computer autoenrollment messages (start, stop) should occur first, followed by two user autoenrollment messages (start, stop)", within about two minutes. Then trigger one run on demand. <code>certutil -pulse</code> "Pulses an autoenrollment event or NGC task." It enrolls if anything is due, the same work the scheduled run does (our note):</p>
<pre><code class="language-console">:: Read-mostly. Trigger autoenrollment now: computer context (elevated), then user context (as the user, not elevated)
certutil -pulse
certutil -user -pulse
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No events after a pulse, even with verbose logging</td>
<td>Policy isn't applied for that context</td>
<td>Step 2</td>
</tr>
<tr>
<td>2 and 3 with nothing in between</td>
<td>Autoenrollment ran and found nothing to do: no template it may autoenroll, or a valid certificate already exists</td>
<td>Step 4</td>
</tr>
<tr>
<td>15 with 0x8007054b</td>
<td>The client couldn't reach a DC</td>
<td>Step 6</td>
</tr>
<tr>
<td>6 / 13 with 0x800706ba or 0x80070005</td>
<td>It found the template and the CA and failed calling the CA</td>
<td>Steps 6, 7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: The template: version, rights, and flags (read-only)</h2>
<p>Three things on the template decide whether a client even tries. Start with what the client can see. Microsoft's wiki: run <code>certutil -Template</code> "when logged in as the end-user to see if the end-user has Read and Enroll permissions (but it will not reveal which certs the user has Autoenroll permissions to)". For computer templates, run it elevated without <code>-user</code>:</p>
<pre><code class="language-console">:: Read-only. Templates the current user can see through enrollment policy (run as the affected user)
certutil -user -Template &gt; C:\Temp\templates-user.txt

:: Read-only. Elevated: the computer's view, then the AD attributes of one template (WorkstationAuth-Contoso is an example name)
certutil -Template &gt; C:\Temp\templates-machine.txt
certutil -dsTemplate WorkstationAuth-Contoso &gt; C:\Temp\dstemplate.txt
</code></pre>
<p><strong>Version.</strong> Microsoft's template concepts page: version 1 templates support autoenrollment only in a narrow case. "Automatic enrollment is only supported for custom scripts and through group policy if using computer certificates." And: "Version 2 templates provide support for certificate autoenrollment." The default User template is version 1, so user autoenrollment needs a duplicate (our reading of Microsoft's default template table).</p>
<p><strong>Rights.</strong> The identity needs Read, Enroll, and Autoenroll. Enroll and Autoenroll are extended rights on the template object. Microsoft's schema pages give their GUIDs: Enroll (<code>Certificate-Enrollment</code>) is <code>0e10c968-78fb-11d2-90d4-00c04f79dc55</code>, and <code>Certificate-AutoEnrollment</code> is <code>a05b8cc2-17bc-4802-a710-e7c15ab866a2</code>. Read them from the template's ACL with the ActiveDirectory module:</p>
<pre><code class="language-powershell"># Read-only. Template attributes and the Enroll / Autoenroll ACEs (template CN is an example; the AD: drive form is ours)
Import-Module ActiveDirectory
$config   = (Get-ADRootDSE).configurationNamingContext
$template = "CN=WorkstationAuth-Contoso,CN=Certificate Templates,CN=Public Key Services,CN=Services,$config"

Get-ADObject -Identity $template -Properties displayName, 'msPKI-Template-Schema-Version', 'msPKI-Enrollment-Flag', flags |
  Select-Object Name, displayName, 'msPKI-Template-Schema-Version', 'msPKI-Enrollment-Flag', flags

$rights = @{ '0e10c968-78fb-11d2-90d4-00c04f79dc55' = 'Enroll'; 'a05b8cc2-17bc-4802-a710-e7c15ab866a2' = 'Autoenroll' }
(Get-Acl -Path "AD:\$template").Access |
  Where-Object { $rights.ContainsKey($_.ObjectType.ToString()) -or $_.ActiveDirectoryRights -match 'GenericRead|GenericAll' } |
  Select-Object IdentityReference, AccessControlType, ActiveDirectoryRights, @{ n = 'ExtendedRight'; e = { $rights[$_.ObjectType.ToString()] } }
</code></pre>
<p>Then check that the failing identity is actually in the group that holds Autoenroll:</p>
<pre><code class="language-powershell"># Read-only. The computer's groups in AD (LAPTOP17 is an example), and the signed-in user's token on the client
Get-ADComputer -Identity LAPTOP17 | Get-ADPrincipalGroupMembership | Select-Object Name
whoami /groups
</code></pre>
<p>A computer added to a group yesterday may not have that group in its token yet. The token is built at startup, so AD membership and token membership can disagree until a restart or a purge of the computer's tickets (our reading; Step 11 has the <code>klist</code> form).</p>
<p><strong>The CA's own Read.</strong> Microsoft KB 283218: "The Authenticated Users group is on a template ACL, by default. (The Certification Authority object itself is included in this group.)" And: "If the Authenticated Users group is removed, the (enterprise) CA itself can no longer read the template in the Active Directory, and that's why certificate requests can be unsuccessful." Its minimums: "Requesting user/machine: Read &amp; Enroll, Certification Authority Object: Read". Autoenrollment adds Autoenroll for the requester.</p>
<p><strong>Flags.</strong> Microsoft's template protocol spec defines the bits in <code>msPKI-Enrollment-Flag</code>. The ones that change autoenrollment:</p>
<table>
<thead>
<tr>
<th>Bit</th>
<th>Name</th>
<th>Microsoft's description</th>
<th>What you'll see (our reading)</th>
</tr>
</thead>
<tbody><tr>
<td>0x2</td>
<td><code>CT_FLAG_PEND_ALL_REQUESTS</code></td>
<td>"This flag instructs the CA to put all requests in a pending state."</td>
<td>Requests sit in Pending Requests (Step 8)</td>
</tr>
<tr>
<td>0x10</td>
<td><code>CT_FLAG_AUTO_ENROLLMENT_CHECK_USER_DS_CERTIFICATE</code></td>
<td>Instructs "clients not to do autoenrollment for a certificate based on this template if the user's userCertificate attribute" in AD "has a valid certificate based on the same template"</td>
<td>A user with a certificate published in AD gets no new one on a new PC</td>
</tr>
<tr>
<td>0x20</td>
<td><code>CT_FLAG_AUTO_ENROLLMENT</code></td>
<td>"This flag instructs clients to perform autoenrollment for the specified template."</td>
<td>Set on templates meant for autoenrollment; it doesn't replace the Autoenroll permission</td>
</tr>
<tr>
<td>0x40000</td>
<td><code>CT_FLAG_SKIP_AUTO_RENEWAL</code></td>
<td>"This flag indicates that the certificate should not be auto-renewed, although it has a valid template."</td>
<td>Initial enrollment works, renewal never happens</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only. Decode the enrollment flags on the template read above (bit values from MS-CRTD; the test form is ours)
$t = Get-ADObject -Identity $template -Properties 'msPKI-Enrollment-Flag'
$f = [int]$t.'msPKI-Enrollment-Flag'
[pscustomobject]@{
  PendAllRequests          = [bool]($f -band 0x2)
  CheckUserDsCertificate   = [bool]($f -band 0x10)
  AutoEnrollmentFlag       = [bool]($f -band 0x20)
  SkipAutoRenewal          = [bool]($f -band 0x40000)
}
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Template version 1 for user certificates</td>
<td>Version 1 autoenrolls only computer certificates</td>
<td>Step 11: duplicate it</td>
</tr>
<tr>
<td>Autoenroll granted to a group, identity not in it</td>
<td>The client skips the template (our reading)</td>
<td>Add the identity to that group, not a new ACE</td>
</tr>
<tr>
<td>Group membership in AD, not in the token</td>
<td>Token predates the change (our reading)</td>
<td>Step 11: restart or <code>klist</code> purge</td>
</tr>
<tr>
<td>Authenticated Users removed, no CA computer Read ACE</td>
<td>The CA can't read the template: 0x80094800</td>
<td>Step 11: "each and every CA's computer account must be added to the template ACLs and set to Read."</td>
</tr>
<tr>
<td><code>PendAllRequests</code> true</td>
<td>CA certificate manager approval</td>
<td>Step 8</td>
</tr>
<tr>
<td><code>SkipAutoRenewal</code> true</td>
<td>No automatic renewal</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: Is the template published on an enterprise CA? (read-only)</h2>
<p>Microsoft: "Only an enterprise CA can issue certificates based on a certificate template." Creating or duplicating a template doesn't offer it; a CA has to issue it. Microsoft's Windows Hello for Business guide says it about supersedence too: "However, the certificate template and the superseding of certificate templates isn't active until the template is published to one or more certificate authorities."</p>
<pre><code class="language-console">:: Read-only. Which CAs publish this template (WorkstationAuth-Contoso is an example)
certutil -TemplateCAs WorkstationAuth-Contoso

:: Read-only. Every template one CA publishes ("CA01.contoso.com\Contoso Issuing CA" is an example config string)
certutil -CATemplates -config "CA01.contoso.com\Contoso Issuing CA"
</code></pre>
<p>Microsoft's descriptions: <code>-TemplateCAs</code> "Displays the certification authorities (CAs) for a certificate template." <code>-CATemplates</code> "Displays templates for the Certificate Authority." The client reads the same list from AD, from the Enrollment Services container. Microsoft's blog points there for 0x800706ba too: "Check network connectivity to all of the available certification authorities listed in the Enrollment Services object". Read it:</p>
<pre><code class="language-powershell"># Read-only. Enterprise CAs registered in AD, their host names, and the templates each one publishes
$config = (Get-ADRootDSE).configurationNamingContext
Get-ADObject -SearchBase "CN=Enrollment Services,CN=Public Key Services,CN=Services,$config" `
  -Filter "objectClass -eq 'pKIEnrollmentService'" -Properties dNSHostName, certificateTemplates |
  Select-Object Name, dNSHostName, @{ n = 'Templates'; e = { $_.certificateTemplates -join ', ' } }
</code></pre>
<p><code>certificateTemplates</code> is an attribute of Microsoft's <code>pKIEnrollmentService</code> schema class. An entry for a CA that no longer exists is a CA the client may still try (our reading; Microsoft's decommission article covers removing it).</p>
<p>Microsoft's wiki also lists a setup check here: the issuing CA's computer account is in the domain's <strong>Cert Publishers</strong> group.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>-TemplateCAs</code> returns nothing</td>
<td>No CA issues the template</td>
<td>Step 11: publish it on one CA</td>
</tr>
<tr>
<td>Published, but only on a CA the client can't reach</td>
<td>Enrollment tries that CA and fails</td>
<td>Step 6 for that CA</td>
</tr>
<tr>
<td>A dead CA still listed in Enrollment Services</td>
<td>Clients may try it first</td>
<td>Microsoft's decommission article, deliberately</td>
</tr>
<tr>
<td>Published on a reachable CA</td>
<td>Publishing isn't the problem</td>
<td>Step 6</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Can the client reach a DC and the CA? (read-only)</h2>
<p><strong>The DC first.</strong> Event 15 with 0x8007054b means the client didn't get as far as the CA. Microsoft's blog: the cause is "problems getting to a DC in the domain, common cause is name resolution." That's the same DC locator path as <a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a>, which covers it from the client side. A laptop that booted off the network and signed in with cached credentials fits Microsoft's 0x80072751 note, too.</p>
<p><strong>Then the CA.</strong> Microsoft's port reference: "Certificate Services relies on RPC and DCOM to communicate with clients by using random TCP ports that are higher than port 1024." Its table for the service lists TCP 135, SMB on TCP 445 and 139, and the random range 49152-65535. Microsoft's firewall guide explains the two steps: the endpoint mapper on 135 "replies with a dynamically assigned port number that the client must use to communicate with the service". A firewall that allows 135 and not the range lets the first step through and blocks the second (our reading).</p>
<p>Microsoft's own end-to-end test is <code>certutil -ping</code>, which "Attempts to contact the Active Directory Certificate Services Request interface." Run it from the failing client:</p>
<pre><code class="language-console">:: Read-only. From the client: contact the CA's request interface (config string and host name are examples)
certutil -ping -config "CA01.contoso.com\Contoso Issuing CA"

:: Read-only. Same test by machine name; the terminating comma marks a single machine (Microsoft's syntax), and it shows site cost
certutil -ping CA01.contoso.com,
</code></pre>
<pre><code class="language-powershell"># Read-only. TCP from the client to the CA: the endpoint mapper and SMB (a passing test proves TCP, not DCOM)
foreach ($port in 135, 445) {
  Test-NetConnection -ComputerName CA01.contoso.com -Port $port -WarningAction SilentlyContinue |
    Select-Object ComputerName, RemoteAddress, RemotePort, TcpTestSucceeded
}

# Read-only. On the CA: is the Certificate Services service running?
Get-Service -Name CertSvc | Select-Object Name, DisplayName, Status, StartType
</code></pre>
<p>Microsoft's blog for 0x800706ba lists the same reads, ending with "Use Portqry to verify that the necessary RPC ports are opened." <code>portqry -n CA01.contoso.com -e 135</code> lists what the CA's endpoint mapper registers; test a listed <code>ncacn_ip_tcp</code> port with <code>-e</code> (our form of that step).</p>
<p><strong>NAT breaks DCOM.</strong> Microsoft's RPC firewall article: "You can't use DCOM through firewalls that do address translation." The reason: "DCOM stores raw IP addresses in the interface marshaling packets." A client that reaches the CA through a NAT'd VPN pool or a published address fails even with every port open (our reading of that article).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Event 15, 0x8007054b</td>
<td>No DC: name resolution or reach</td>
<td>The Netlogon 5719 post</td>
</tr>
<tr>
<td><code>certutil -ping</code> fails, 135 fails</td>
<td>The CA is down, unreachable, or 135 is blocked</td>
<td>Network path; <code>CertSvc</code> on the CA</td>
</tr>
<tr>
<td>135 succeeds, <code>-ping</code> fails</td>
<td>The dynamic range is blocked, or DCOM refuses the call (our reading)</td>
<td>PortQry the listed port, then Step 7</td>
</tr>
<tr>
<td>Works on the LAN, fails over VPN</td>
<td>NAT or a VPN firewall policy without the range (our reading)</td>
<td>VPN path design; Microsoft's NAT note</td>
</tr>
<tr>
<td><code>CertSvc</code> stopped</td>
<td>Nothing answers on the CA</td>
<td>Read the CA's System and Application logs for why it stopped before you restart it (our reading)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: DCOM and access on the CA: the 0x800706ba that isn't the network (read-only)</h2>
<p>Microsoft's article for 0x800706ba during enrollment describes a failure that looks like the network and isn't. In its network trace, the client's LDAP queries to the configuration partition succeed, and then the RPC call to the CA comes back access denied. Its note on a tell: "When the issue occurs, if we add the user account that's used to request the certificate to the local administrators group on the certificate authority (CA), the enrollment succeeds for a user-based template. However, enrollment against a machine-based template still returns the same error." That's why adding users to the CA's Administrators group looks like a fix and isn't one.</p>
<p>On the CA, Microsoft's article logs this when auditing is on, as DCOM event 10027 in the System log: "The machine wide limit settings do not grant Remote Activation permission for COM Server applications to the user NT AUTHORITY\ANONYMOUS LOGON SID (S-1-5-7)". Read for it, and for the DCOM hardening events from KB5004442:</p>
<pre><code class="language-powershell"># Read-only. On the CA: DCOM 10027 (Microsoft's 0x800706ba article) and 10036 (KB5004442 server event), last 7 days
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 10027, 10036; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, ProviderName, Message | Format-List

# Read-only. On the failing client: KB5004442 client events 10037 / 10038
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 10037, 10038; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message
</code></pre>
<p>KB5004442: "The system will log these events if it detects that a DCOM client application is trying to activate a DCOM server using an authentication level that is less than RPC_C_AUTHN_LEVEL_PKT_INTEGRITY." Its timeline ends with "March 14, 2023 Phase 3 Release - Hardening changes enabled by default with no ability to disable them." So the old registry opt-out isn't a fix on a patched CA. Microsoft's client-side patch raises the level for most Windows clients automatically, so unpatched clients and non-Windows apps are the ones to look at (our reading of KB5004442).</p>
<p>Microsoft's six causes for the access-denied form of 0x800706ba, and the read for each, on the CA:</p>
<table>
<thead>
<tr>
<th>Cause (Microsoft's article)</th>
<th>Read-only check</th>
<th>Healthy value</th>
</tr>
</thead>
<tbody><tr>
<td>1. <strong>Access this computer from the network</strong> doesn't include the requester, or <strong>Deny access to this computer from the network</strong> includes a group it's in</td>
<td><code>gpresult /h</code> on the CA, User Rights Assignment, note the <strong>Winning GPO</strong></td>
<td>Microsoft's default list: Administrators, Backup Operators, Everyone, and Users; no requester group in Deny</td>
</tr>
<tr>
<td>2. Authenticated Users missing from the CA's local <strong>Users</strong> group</td>
<td><code>Get-LocalGroupMember -Group Users</code></td>
<td>Domain Users, Authenticated Users, INTERACTIVE</td>
</tr>
<tr>
<td>3. Authenticated Users missing from <strong>Certificate Service DCOM Access</strong></td>
<td><code>Get-LocalGroupMember -Group 'Certificate Service DCOM Access'</code></td>
<td>Authenticated Users present</td>
</tr>
<tr>
<td>4. <code>EnableDCOM</code> not <code>Y</code> on the client or the CA</td>
<td><code>HKLM\Software\Microsoft\OLE</code>, value <code>EnableDCOM</code></td>
<td><code>Y</code></td>
</tr>
<tr>
<td>5. <strong>Restrictions for Unauthenticated RPC Client</strong>: Microsoft's cause title is "Remote Procedure Call restrictions aren't applied on the Certificate server"</td>
<td><code>gpresult /h</code> on the CA, Administrative Templates \ System \ Remote Procedure Call; note the winning GPO and its value</td>
<td>Compare with Microsoft's article before changing it; its wording is terse (our note)</td>
</tr>
<tr>
<td>6. <strong>Certificate Service DCOM Access</strong> removed from the COM Security limits</td>
<td>Component Services &gt; My Computer &gt; COM Security &gt; Edit Limits</td>
<td>Local and Remote Access; Local and Remote Activation</td>
</tr>
</tbody></table>
<pre><code class="language-console">:: Read-only. On the CA, elevated: the applied policy report (Microsoft's form)
gpresult /h C:\Temp\appliedgpo-ca.html
</code></pre>
<pre><code class="language-powershell"># Read-only. On the CA: the two local groups and EnableDCOM (group names as Microsoft's article gives them)
Get-LocalGroupMember -Group 'Certificate Service DCOM Access' | Select-Object Name, ObjectClass, PrincipalSource
Get-LocalGroupMember -Group 'Users' | Select-Object Name, ObjectClass
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Ole' -Name EnableDCOM
</code></pre>
<p>On a CA installed on a DC, Microsoft's older blog says the DCOM group "may be a Domain Local group instead"; read it with <code>Get-ADGroupMember</code> (our form). The same blog lists Domain Users and Domain Computers as members to check, plus users and computers from other domains in the forest.</p>
<p>Microsoft's article has one line worth repeating before anyone edits a policy on a CA: "The settings configured on the Group Policy Objects (GPOs) are for a reason, so you should talk to your security team before making any changes."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>10027 ANONYMOUS LOGON on the CA</td>
<td>Kerberos failed for the caller and it fell back to anonymous, which DCOM refuses</td>
<td>Causes 1 and 2 first</td>
</tr>
<tr>
<td>Requester's group in <strong>Deny access to this computer from the network</strong></td>
<td>Cause 1</td>
<td>Security team, then the winning GPO (Step 11)</td>
</tr>
<tr>
<td>Authenticated Users missing from either group</td>
<td>Causes 2 and 3</td>
<td>Step 11: add it back</td>
</tr>
<tr>
<td><code>EnableDCOM</code> = <code>N</code></td>
<td>Cause 4</td>
<td>Step 11</td>
</tr>
<tr>
<td>COM Security limits greyed out</td>
<td>Set by policy. In a Microsoft Q&amp;A answer, the DCOM SDDL security options are the place to look (community)</td>
<td>The GPO that sets them</td>
</tr>
<tr>
<td>10036 on the CA, 10037/10038 on clients</td>
<td>DCOM hardening rejected a low authentication level</td>
<td>Patch the client or the app (our reading)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: Did the request reach the CA, and what did the CA do? (read-only)</h2>
<p>If the client got through, the CA has a record. Microsoft's wiki: make sure the request isn't in pending or failed status in the Certification Authority console. Look in <strong>Pending Requests</strong> and <strong>Failed Requests</strong> for the requester, and read the CA's Application log:</p>
<pre><code class="language-powershell"># Read-only. On the CA: CertificationAuthority events (53 = denied, 77 = template couldn't load, 80 = couldn't publish to AD; IDs from Microsoft's pages)
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; Id = 53, 77, 80; StartTime = (Get-Date).AddDays(-7) } -ErrorAction SilentlyContinue |
  Where-Object ProviderName -like '*CertificationAuthority*' |
  Select-Object TimeCreated, Id, Message | Format-List
</code></pre>
<table>
<thead>
<tr>
<th>CA event</th>
<th>Microsoft's text</th>
<th>Means</th>
</tr>
</thead>
<tbody><tr>
<td>53</td>
<td>"Active Directory Certificate Services denied request" ... "because The requested certificate template is not supported by this CA."</td>
<td>The CA can't use the template: not published, or it can't read it (Steps 4, 5)</td>
</tr>
<tr>
<td>77</td>
<td>"Certificate Template could not be loaded. Element not found."</td>
<td>The CA lost Read on the template (KB 283218)</td>
</tr>
<tr>
<td>80</td>
<td>"Active Directory Certificate Services could not publish a Certificate for request" ... "Insufficient access rights to perform the operation."</td>
<td>The certificate issued but couldn't be written to the user object in AD</td>
</tr>
</tbody></table>
<p>For 80, Microsoft's blog: "Check that the Cert Publishers group has permission to read and write to the userCertificate attribute on the user object in AD that is specified in the event."</p>
<p><strong>Pending forever.</strong> A template with <strong>CA certificate manager approval</strong> (the 0x2 flag in Step 4) holds every request until someone issues it. Microsoft documents a renewal trap with it: "When unsupported values of validity and renewal period are configured in a certificate template, the certificate renewal is skipped and the client triggers new enrollment requests instead of renewals, then prompting CA certificate manager approval." Its guidance: "For normal renewal behaviors, set the renewal period to more than 8 hours and less than 20% of the validity period." Read the two values from the <code>certutil -dsTemplate</code> output in Step 4: <code>pKIExpirationPeriod</code> is the validity and <code>pKIOverlapPeriod</code> is the renewal period (Microsoft's note on that article).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Pending</td>
<td>Manager approval on the template</td>
<td>Issue it, or Step 11 for the template</td>
</tr>
<tr>
<td>Failed with 0x80094800</td>
<td>Microsoft's KB 283218</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>New request every 8 hours, each pending</td>
<td>Microsoft's renewal-period trap above</td>
<td>Step 11: renewal period</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Chain, revocation, expiry, and renewal timing (read-only)</h2>
<p><strong>Revocation.</strong> Microsoft's autoenrollment blog: "The certificate request failed. The revocation function was unable to check revocation because the revocation server was offline." Its explanation: the client doesn't have a valid CRL from the issuing CA. Its test runs <code>certutil -v -verify -urlfetch</code> against the issuing CA's certificate. Get the CA certificate from the CA, then verify it with URL fetching:</p>
<pre><code class="language-console">:: Read-only (writes one local file). The issuing CA's certificate, then verify its chain and fetch every CDP/AIA URL
certutil -config "CA01.contoso.com\Contoso Issuing CA" -ca.cert C:\Temp\issuing-ca.cer
certutil -v -verify -urlfetch C:\Temp\issuing-ca.cer &gt; C:\Temp\verify-issuing-ca.txt
</code></pre>
<p>Microsoft's caution on that test: "it will use the security and proxy settings of the user that runs it". The computer account can still fail if your admin session passes (our reading).</p>
<p><strong>Expiry and renewal.</strong> Microsoft: "Certificate autoenrollment runs every eight hours." And: "To be renewed, a certificate should have completed 80% of its validity period and be within the renewal period." Read what's in the store and what's about to lapse:</p>
<pre><code class="language-powershell"># Read-only. Computer certificates with expiry and the template extension (filtering on the extension's friendly name is our form)
Get-ChildItem -Path Cert:\LocalMachine\My |
  Select-Object Subject, Issuer, NotAfter, Thumbprint,
    @{ n = 'Template'; e = { ($_.Extensions | Where-Object { $_.Oid.FriendlyName -like 'Certificate Template*' } | ForEach-Object { $_.Format($false) }) -join '; ' } }

# Read-only. Anything that expires in the next 30 days (the certificate provider's -ExpiringInDays parameter)
Get-ChildItem -Path Cert:\LocalMachine\My -ExpiringInDays 30 | Select-Object Subject, NotAfter, Thumbprint
</code></pre>
<p>Event 64 gives a thumbprint; a Microsoft Q&amp;A answer finds it with <code>Get-ChildItem cert:\*&lt;thumbprint&gt; -recurse</code> (community). The warning threshold comes from the policy's expiration percentage. Microsoft's cmdlet page: it "Sets the percentage of the certificate lifetime at which close-to-expiration events are logged and auto-enrollment notifications start to appear." (<code>ExpirationPercentage</code> in Step 2's output.)</p>
<p><strong>Supersedence leftovers.</strong> Old user certificates published in AD aren't removed when a template is superseded. Microsoft KB 2884551: "This feature has been removed in Vista and subsequent Windows versions." That matters with the 0x10 flag in Step 4 (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>-urlfetch</code> fails on a CDP URL</td>
<td>Expired or unreachable CRL; Microsoft's revocation error</td>
<td>Step 11: publish a CRL on the CA, fix the URL path</td>
</tr>
<tr>
<td>Certificate past 80% and inside the renewal period, no renewal</td>
<td>Renewal blocked: rights (Enroll still needed), skip-renewal flag, or Steps 6-8</td>
<td>Steps 4, 6-8</td>
</tr>
<tr>
<td>Event 64 for a certificate nobody uses</td>
<td>A stale certificate in a monitored store</td>
<td>Confirm the owner, then archive or remove it</td>
</tr>
<tr>
<td>Two certificates from old and new templates</td>
<td>Supersedence not published, or old certificate still valid</td>
<td>Steps 5, 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Domain controller certificates (read-only)</h2>
<p>DCs behave differently enough to read separately. Microsoft: "Domain controllers automatically request a domain controller certificate (if published) when they discover an enterprise CA is added to Active Directory." And the catch: "However, domain controllers are unaware of newer certificate templates or superseded configurations on certificate templates. For domain controllers to automatically enroll and renew of certificates, configure a GPO for automatic certificate enrollment, and link it to the Domain Controllers OU."</p>
<p>The current template is Kerberos Authentication. Microsoft: "The Kerberos Authentication certificate template is the most current certificate template designated for domain controllers, and should be the one you deploy to all your domain controllers." The older Domain Controller and Domain Controller Authentication templates "don't include the KDC Authentication object identifier (OID), which was later added to the Kerberos RFC." Microsoft's EKU list for a DC certificate: Client Authentication (1.3.6.1.5.5.7.3.2), Server Authentication (1.3.6.1.5.5.7.3.1), and KDC Authentication (1.3.6.1.5.2.3.5), with the DNS name in the Subject Alternative Name.</p>
<p>Microsoft's reads on a DC:</p>
<pre><code class="language-console">:: Read-only. On a DC, elevated: enrolled and archived certificates, verbose, with templates and EKUs
certutil -q -v -store my &gt; C:\Temp\dc-store.txt

:: Read-only. Every DC's certificates, verified (DeleteBad and DeleteAll are the destructive modifiers; don't use them here)
certutil -DCInfo Verify &gt; C:\Temp\dcinfo.txt

:: Read-only. The forest's NTAuth store (Microsoft's form)
certutil -viewstore -enterprise NTAuth
</code></pre>
<p>Microsoft on the first two: "Certutil shows enrolled and archived certificates for the local computer." The console doesn't: "Archived certificates don't appear in Certificate Manager." <code>-DCInfo</code> "Displays information about the domain controller. The default displays DC certificates without verification." And on NTAuth: "If the domain controller certificate does not chain to a root in the NTAuth store, user authentication will fail."</p>
<p>Microsoft's validation also points at a log: <strong>Application and Services &gt; Microsoft &gt; Windows &gt; CertificateServices-Lifecycles-System</strong>, where a new autoenrollment shows the template, thumbprint, and EKUs, and a superseded certificate shows an archive event. Step 3's <code>-ListLog</code> read shows the exact channel name on your build.</p>
<p>If the DCs use a non-Microsoft CA, Microsoft KB 291010: "Third-party CAs don't support the automatic enrollment and renewal of domain controller or computer certificates."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>DCs only have a Domain Controller template certificate</td>
<td>No DC autoenrollment GPO, or the Kerberos Authentication-based template isn't published or doesn't supersede</td>
<td>Step 11: the DC sequence</td>
</tr>
<tr>
<td>GPO on the Domain Controllers OU, still no new certificate</td>
<td>The template lacks Autoenroll for Domain Controllers, or isn't published (our reading)</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>Root missing from NTAuth</td>
<td>Microsoft: user authentication fails</td>
<td>Microsoft's NTAuth import article for non-Microsoft CAs</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Full Control (or Write) on the template for Domain Computers</strong></td>
<td>Autoenrollment needs Read, Enroll, and Autoenroll. Write lets every computer change what the CA issues for that template (our view)</td>
<td>Grant exactly Read, Enroll, Autoenroll to the group that should enroll (Step 4)</td>
</tr>
<tr>
<td><strong>Adding users to the CA's local Administrators group</strong></td>
<td>Microsoft's 0x800706ba article: it makes user-template enrollment succeed while machine-template enrollment "still returns the same error." It also hands out admin on a CA (our view)</td>
<td>Read the six causes (Step 7)</td>
</tr>
<tr>
<td><strong><code>certreq</code> or the MMC wizard on every machine</strong></td>
<td>It hides the broken link and won't renew on its own if the link stays broken (our reading)</td>
<td>One manual enrollment on one machine as a test, as Microsoft's blog suggests for 0x800706ba</td>
</tr>
<tr>
<td><strong>Turning off the firewall on the CA</strong></td>
<td>Shows the path matters, not which rule was missing (our view)</td>
<td><code>certutil -ping</code> and PortQry from the client (Step 6)</td>
</tr>
<tr>
<td><strong>Disabling DCOM hardening with the registry value</strong></td>
<td>KB5004442 Phase 3 removed the opt-out</td>
<td>Find the client or app on 10037 / 10038 (Step 7)</td>
</tr>
<tr>
<td><strong><code>certutil -DCInfo DeleteBad</code> or <code>DeleteAll</code> while reading</strong></td>
<td>Those modifiers delete DC certificates</td>
<td><code>certutil -DCInfo Verify</code> first (Step 10)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 11: Change one link, then prove it (only after the read)</h2>
<p>Pick the one change Steps 1-10 point at. Save the current state first:</p>
<pre><code class="language-powershell"># Low-risk write. Save the template attributes, the autoenrollment keys, and the CA's published templates (our habit)
New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null
certutil -dsTemplate WorkstationAuth-Contoso &gt; C:\Temp\dstemplate-before.txt
certutil -CATemplates -config "CA01.contoso.com\Contoso Issuing CA" &gt; C:\Temp\catemplates-before.txt
reg export 'HKLM\SOFTWARE\Policies\Microsoft\Cryptography\AutoEnrollment' C:\Temp\ae-policy-before.reg /y
</code></pre>
<p><strong>Policy (Step 2).</strong> In the GPO that targets the right OU, open <strong>Public Key Policies</strong> &gt; <strong>Certificate Services Client - Auto-Enrollment</strong> in the half you need (Computer Configuration for computer certificates, User Configuration for user certificates), set <strong>Configuration Model</strong> to <strong>Enabled</strong>, and select both boxes. Microsoft: "Computers apply the GPO and download the certificate the next time Group Policy is refreshed." For DCs, use Microsoft's separate GPO linked to the Domain Controllers OU (below).</p>
<p><code>Set-CertificateAutoEnrollmentPolicy</code> "configures local certificate auto-enrollment policy for a user or computer." Use it (with <code>-WhatIf</code> first) only on one lab machine, to separate a GPO problem from a template problem (our reading).</p>
<p><strong>Template version (Step 4).</strong> For user certificates on a version 1 template, duplicate it in the Certificate Templates console, set the rights on the copy, and publish the copy. If the copy replaces the old one, add the old one on the copy's <strong>Superseded Templates</strong> tab (Microsoft's DC steps use the same tab).</p>
<p><strong>Template rights (Step 4).</strong> On the template's <strong>Security</strong> tab, give the group that should enroll <strong>Read</strong>, <strong>Enroll</strong>, and <strong>Autoenroll</strong>. Microsoft's NPS template steps are the pattern: for the server group, "ensure that Enroll is selected, then select the check box for Autoenroll." If Authenticated Users was removed on purpose, Microsoft's KB 283218 fix: "each and every CA's computer account must be added to the template ACLs and set to Read."</p>
<p><strong>Token refresh after a group change (Step 4).</strong> The computer's tickets carry its groups. Microsoft's <code>klist</code> page gives the purge for the computer's logon session; a restart does the same (our reading):</p>
<pre><code class="language-console">:: Change, on the client (elevated). Purge the computer account's Kerberos tickets, then pulse autoenrollment
klist purge -li 0x3e7
certutil -pulse
</code></pre>
<p>For a user, sign out and back in.</p>
<p><strong>Publish the template (Step 5).</strong> In the Certification Authority console: <strong>Certificate Templates</strong> &gt; <strong>New</strong> &gt; <strong>Certificate Template to Issue</strong>. Or from the CA, <code>certutil -SetCATemplates</code>, which "Sets the certificate templates that the Certificate Authority can issue." (<code>+</code> adds, <code>-</code> removes):</p>
<pre><code class="language-console">:: Change, on the CA. Add one template to the CA's list (WorkstationAuth-Contoso is an example), then read the list back
certutil -SetCATemplates +WorkstationAuth-Contoso
certutil -CATemplates
</code></pre>
<p><strong>The path to the CA (Step 6).</strong> Ask for a rule from the client subnets to the CA: 135/TCP and 49152-65535/TCP, per Microsoft's port reference. No NAT between DCOM clients and the CA. Then rerun <code>certutil -ping</code> from a client in that subnet.</p>
<p><strong>Access on the CA (Step 7).</strong> Talk to the security team first, as Microsoft's article says. Then fix only the cause the read found: remove the requester's group from the Deny user right in the winning GPO, add Authenticated Users back to the Users or Certificate Service DCOM Access group, set <code>EnableDCOM</code> to <code>Y</code> and restart, or restore the COM Security limits for the DCOM group. For the group:</p>
<pre><code class="language-powershell"># Change, on the CA. Dry-run first (Microsoft's cause 3)
Add-LocalGroupMember -Group 'Certificate Service DCOM Access' -Member 'NT AUTHORITY\Authenticated Users' -WhatIf
</code></pre>
<p>Microsoft's blog: "If you had to change the permissions/members of the CertSVC_DCOM_ACCESS group then you may in certain cases need to run the following to get the CA to recognize the updated DCOM security settings." Its sequence, on the CA, in a maintenance window:</p>
<pre><code class="language-console">:: Change, on the CA. Microsoft's blog sequence after a DCOM group change; restarting CertSvc pauses issuance briefly
certutil -setreg SetupStatus -SETUP_DCOM_SECURITY_UPDATED_FLAG
net stop certsvc
net start certsvc
</code></pre>
<p><strong>Approval and renewal period (Step 8).</strong> Issue the pending request, then decide whether the template should require approval at all. If renewals keep landing as new pending requests, set the renewal period inside Microsoft's range: more than 8 hours and less than 20% of the validity period.</p>
<p><strong>Revocation (Step 9).</strong> On the CA, <code>certutil -CRL</code> "Publishes new certificate revocation lists (CRLs) or delta CRLs." Then copy the CRL to every CDP location the certificates point at (HTTP paths aren't updated by the CA on their own unless you configured it; our reading), and rerun <code>certutil -verify -urlfetch</code> from a client.</p>
<p><strong>Domain controllers (Step 10).</strong> Microsoft's sequence: duplicate <strong>Kerberos Authentication</strong>, add <strong>Domain Controller</strong>, <strong>Domain Controller Authentication</strong>, and <strong>Kerberos Authentication</strong> on the new template's <strong>Superseded Templates</strong> tab, publish it on the CA, and link a GPO named, for example, <strong>Domain Controller Auto Certificate Enrollment</strong> to the <strong>Domain Controllers</strong> OU with autoenrollment enabled. Microsoft's guide then says to unpublish the superseded templates. Then on one DC: "Alternatively, you can forcefully trigger automatic certificate enrollment using certreq.exe -autoenroll -q from an elevated command prompt."</p>
<p><strong>Then prove it.</strong> One machine, one template, one pulse, then the same reads as Steps 3 and 9:</p>
<pre><code class="language-console">:: Read-mostly. Trigger autoenrollment on one test machine (elevated); user context: certutil -user -pulse as the user
gpupdate /target:computer /force
certutil -pulse
</code></pre>
<pre><code class="language-powershell"># Read-only. Events 2/3 (and no 6/13/15) since the pulse, and the new certificate with its template
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; StartTime = (Get-Date).AddMinutes(-15) } -ErrorAction SilentlyContinue |
  Where-Object ProviderName -like '*CertificateServicesClient*' | Select-Object TimeCreated, Id, Message
Get-ChildItem -Path Cert:\LocalMachine\My | Sort-Object NotBefore -Descending | Select-Object -First 3 Subject, Issuer, NotBefore, NotAfter

# Low-risk write. Remove the verbose logging value from Step 3 (dry-run first; our habit)
Remove-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Cryptography\AutoEnrollment' -Name AEEventLogLevel -WhatIf
</code></pre>
<p><code>gpupdate /force</code> "Reapplies all policy settings. By default, only policy settings that have changed are applied." Then let the next scheduled run handle a second machine in the group, so you know it works without a pulse, and confirm the thing the user waited for, like the 802.1X connection (our habit).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Identity lacks Autoenroll</td>
<td>Read, Enroll, Autoenroll for the intended group</td>
<td>Full Control or Write on the template</td>
</tr>
<tr>
<td>CA can't read the template</td>
<td>Authenticated Users back, or every CA computer account with Read</td>
<td>Removing ACEs to "tighten" without the CA's Read</td>
</tr>
<tr>
<td>135 or the range blocked</td>
<td>A client-to-CA rule for 135 and 49152-65535</td>
<td>Turning off the CA's firewall</td>
</tr>
<tr>
<td>Access denied on the CA</td>
<td>The one cause from Microsoft's six, with the security team</td>
<td>Requesters in the CA's Administrators group</td>
</tr>
<tr>
<td>DCs on the old template</td>
<td>Kerberos Authentication-based template, superseding, DC GPO</td>
<td>Deleting DC certificates with <code>-DCInfo DeleteAll</code></td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why is certificate autoenrollment not working?</strong>
Usually one link in the chain: the policy isn't applied for that context, the identity lacks Autoenroll on the template, the template isn't published on a CA, the client can't reach the CA, or the CA refuses the caller over DCOM. The AutoEnrollment event's error code in the Application log tells you which link to read first.</p>
<p><strong>How do I force certificate autoenrollment?</strong>
<code>certutil -pulse</code> (elevated) for the computer and <code>certutil -user -pulse</code> for the signed-in user. Microsoft's DC guide also gives <code>certreq.exe -autoenroll -q</code> from an elevated prompt. <code>gpupdate /force</code> triggers it too, because Windows runs autoenrollment when Group Policy updates.</p>
<p><strong>What permissions does a certificate template need for autoenrollment?</strong>
Read, Enroll, and Autoenroll for the user or computer (or a group it's in, with the group in its token). The CA's computer account needs Read, which Authenticated Users provides by default. For user certificates the template must be version 2 or later.</p>
<p><strong>What does Event ID 6 with 0x800706ba mean?</strong>
Autoenrollment found a template and a CA and the call to the CA failed with "The RPC server is unavailable." Check the network path first with <code>certutil -ping</code>, then the access causes on the CA in Microsoft's 0x800706ba article: network logon rights, the Certificate Service DCOM Access group, <code>EnableDCOM</code>, and the COM Security limits.</p>
<p><strong>Why don't domain controllers get the new Kerberos Authentication certificate?</strong>
DCs request the old Domain Controller template on their own, but Microsoft says they're unaware of newer or superseding templates. Publish a template based on Kerberos Authentication that supersedes the old ones, and link an autoenrollment GPO to the Domain Controllers OU.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/intune-scep-certificate-profile-failed-a-read-first-assignment-and-ndes-triage-4ibc">Intune SCEP Certificate Profile Failed</a> (the Intune path: SCEP profiles, NDES, and the connector instead of Group Policy autoenrollment)</li>
<li><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> (when the Auto-Enrollment setting never reaches the client)</li>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (the DC locator side of Event 15 and 0x8007054b)</li>
<li><a href="https://dev.to/adminpackstudio/intune-wi-fi-profile-not-applying-a-read-first-assignment-and-eap-triage-d1">Intune Wi-Fi Profile Not Applying</a> (EAP-TLS Wi-Fi is often the thing waiting on the certificate)</li>
</ul>
<h2>Free checklist</h2>
<p>A template permission pushed to every computer before one pilot machine got its certificate is a change nobody tested where the enrollment happens. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=certificate-autoenrollment-not-working">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=certificate-autoenrollment-not-working</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=certificate-autoenrollment-not-working">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=certificate-autoenrollment-not-working</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-10 favor read-only checks: <code>gpresult</code>, <code>Get-CertificateAutoEnrollmentPolicy</code>, <code>Get-ItemProperty</code>, <code>Get-WinEvent</code>, <code>certutil -Template</code>, <code>-dsTemplate</code>, <code>-TemplateCAs</code>, <code>-CATemplates</code>, <code>-ping</code>, <code>-ca.cert</code>, <code>-verify -urlfetch</code>, <code>-store</code>, <code>-DCInfo Verify</code>, <code>-viewstore</code>, <code>Get-ADRootDSE</code>, <code>Get-ADObject</code>, <code>Get-Acl</code>, <code>Get-ADComputer</code>, <code>Get-ADPrincipalGroupMembership</code>, <code>whoami /groups</code>, <code>Test-NetConnection</code>, <code>portqry</code>, <code>Get-Service</code>, <code>Get-LocalGroupMember</code>, and <code>Get-ChildItem Cert:</code>. <code>certutil -pulse</code>, <code>certutil -user -pulse</code>, and <code>certreq -autoenroll</code> trigger enrollment; output redirection and <code>reg export</code> write local files. <code>AEEventLogLevel</code>, GPO and local autoenrollment policy, template versions, rights, and supersedence, <code>klist purge</code>, <code>certutil -SetCATemplates</code>, firewall rules, user rights, local group membership, <code>EnableDCOM</code>, COM Security limits, <code>certutil -setreg</code>, CertSvc restarts, <code>certutil -CRL</code>, and DC templates and GPOs are deliberate changes. Change one link at a time, test with one machine first, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Active Directory Replication Error 1722: A Read-First DNS, RPC Port, Firewall, and NIC Triage]]></title><description><![CDATA[AD replication error 1722, the RPC server is unavailable? Read repadmin, the partner's DNS record, port 135, the RPC range, and NICs first.]]></description><link>https://adminpackstudio.hashnode.dev/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/active-directory-replication-error-1722-a-read-first-dns-rpc-port-firewall-and-nic-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 12:41:58 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Two users were added to the Finance-Approvers group on DC1 this morning, and the branch office still can't open the approvals share. Someone ran repadmin and it says 1722, the RPC server is unavailable, from DC1 to BR-DC2. Last success was Thursday. The network team says nothing changed. A blog post says to restrict the RPC port range in the registry, force replication with /force, and if that fails, demote and re-promote the branch DC. Can we start with the registry?"</p>
<p>Not yet. <strong>Error 1722 isn't an Active Directory fault. It's RPC reporting that a lower layer failed: the destination DC couldn't open a connection to the source DC.</strong> Microsoft: "RPC error 1722/0x6ba/RPC_S_SERVER_UNAVAILABLE is logged when a lower-layer protocol reports a connectivity failure." And: "In the context of AD replication, the RPC client on the destination DC was not able to successfully connect to the RPC server on the source DC." Changing the RPC port range before you know which lower layer failed adds a new variable to an outage you haven't diagnosed yet. Work through it as a chain: <strong>which partnership, which direction, since when</strong> → <strong><code>repadmin /replsummary</code> and <code>/showrepl</code>: the exact status per partner and partition</strong> → <strong>the Directory Service events</strong> → <strong>DNS: does the destination resolve the source's GUID-based CNAME to the right IP?</strong> → <strong>the NICs and DNS registration on both DCs</strong> → <strong>port 135, the RPC dynamic range, and the firewalls between them</strong> → <strong>the RPC server side: services and the endpoint the source registered</strong> → <strong>the sibling, "The target principal name is incorrect"</strong> → <strong>is the partner still a real, current DC?</strong> <code>repadmin</code>, <code>dcdiag</code>, <code>Resolve-DnsName</code>, <code>PortQry</code>, <code>Test-NetConnection</code>, <code>Get-ADReplicationFailure</code>, and <code>Get-WinEvent</code> are the sources for that chain. Demote and re-promote a DC behind the same firewall rule, and the new DC fails with the same 1722.</p>
<p>If the problem is a member computer that can't find or reach a DC at boot, read <a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> first. It covers the DC locator, DNS, and ports from the client side. If SYSVOL or DFS-replicated folders are the ones out of date, that's DFS Replication, covered in <a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication Not Working</a>. This post covers <strong>"Active Directory replication between two domain controllers fails with 1722, the RPC server is unavailable (or its sibling, the target principal name is incorrect), and you need a read-first path before you change the RPC range, force replication, or rebuild a DC."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>1722 is a connection failure, mapped by RPC.</strong> Microsoft: "RPC itself has no special insight into failures but attempts to map lower-layer protocol failures into an error at the RPC layer." Find the lower layer: DNS, routing, a firewall, the NIC, or a service that isn't running.</li>
<li><strong>DNS first.</strong> Microsoft: "Domain Name System (DNS) lookup failures are the cause of a large number of 1722 RPC errors when it comes to replication." The destination looks up the source by its GUID-based CNAME in <code>_msdcs</code>. Resolve that name from the destination, against each of its DNS servers.</li>
<li><strong>Then port 135 and the dynamic range.</strong> Microsoft: "The endpoint mapper (listening on port 135) tells the client which randomly assigned port a service (FRS, AD replication, MAPI, and so on) is listening on." On current Windows that range starts at 49152. A firewall that allows 135 but not the range still breaks replication.</li>
<li><strong>Neighboring codes point at different layers.</strong> 1753 means 135 answered but the replication interface wasn't registered. 1727 means the bind to the replication interface didn't finish. 8524 is a DNS lookup failure. -2146893022 is Kerberos, not the network (Step 2).</li>
<li><strong>"The target principal name is incorrect" is usually a password mismatch.</strong> Microsoft's top cause: "the KDC has an old version of the password for the source domain controller." Its fix is about the KDC, not the network (Step 8).</li>
<li><strong>Prove it with one partnership.</strong> After a change, <code>repadmin /replicate</code> one destination from one source for one partition, then <code>/showrepl</code> and <code>/replsummary</code> again.</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Which destination, which source, which partition, since when, what changed</td>
<td>"AD replication is broken" with no DC names</td>
</tr>
<tr>
<td>2. Replication status</td>
<td><code>/showrepl</code> names the failing partner and the exact status</td>
<td>1722 on every partner, or just one</td>
</tr>
<tr>
<td>3. Events</td>
<td>Directory Service log read on the destination</td>
<td>KCC events 1311, 1865, 1925 nobody has read</td>
</tr>
<tr>
<td>4. DNS</td>
<td>The source's CNAME and A record resolve to its current IP from every DNS server the destination uses</td>
<td>Stale A record; CNAME missing; hosts file entry</td>
</tr>
<tr>
<td>5. NICs and registration</td>
<td>One registered adapter per DC; DNS client settings point at internal DNS</td>
<td>Multihomed DC registers the wrong IP</td>
</tr>
<tr>
<td>6. Ports and RPC range</td>
<td>135 and the source's replication port reachable from the destination</td>
<td>135 filtered; the dynamic range blocked between sites</td>
</tr>
<tr>
<td>7. RPC server side</td>
<td>AD DS and RPC running on the source; its replication interface registered</td>
<td>Source DC hung at boot; services stopped</td>
</tr>
<tr>
<td>8. Target principal name</td>
<td>The source's password matches the KDC's copy</td>
<td>-2146893022 after a restore or long outage</td>
</tr>
<tr>
<td>9. Partner still real</td>
<td>The source is online, current, and inside the tombstone lifetime</td>
<td>A decommissioned DC still listed as a partner</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one layer, one change</td>
<td>Registry ports changed, DC demoted, same 1722</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Destination DC</strong> (the one reporting the error)</td>
<td>The destination is the RPC client; most reads run from its console</td>
</tr>
<tr>
<td><strong>Source DC</strong> (the partner it can't reach)</td>
<td>The RPC server; Step 7 runs on its console</td>
</tr>
<tr>
<td><strong>Partition</strong> (domain, Configuration, Schema, DNS zones)</td>
<td>Different partitions, same failure: the path. One partition only: points away from the network (our reading)</td>
</tr>
<tr>
<td><strong>Last success time</strong></td>
<td>Hours: a change. Weeks: Step 9 (tombstone lifetime) before you force anything</td>
</tr>
<tr>
<td><strong>One partner or all of them?</strong></td>
<td>One partner: that path or that source. Every partner: the destination's own DNS, NIC, or firewall</td>
</tr>
<tr>
<td><strong>What changed?</strong></td>
<td>Firewall rules, a new VLAN or site link, a new NIC or NIC driver, DNS server changes, a restored or re-IP'd DC</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>1722 from one source, other partners fine</td>
<td>The path to that source, or the source itself</td>
<td>Steps 4, 6, 7</td>
</tr>
<tr>
<td>1722 from every source on one destination</td>
<td>The destination's DNS client settings, NIC, or local firewall</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>1722 only across a site link</td>
<td>Routing or a firewall between sites, often the dynamic range</td>
<td>Step 6</td>
</tr>
<tr>
<td>1722 right after a DC was re-IP'd or restored</td>
<td>Stale A record or stale NTDS Settings object</td>
<td>Steps 4, 9</td>
</tr>
<tr>
<td>8524 next to the 1722</td>
<td>Microsoft: "The DSA operation is unable to proceed because of a DNS lookup failure."</td>
<td>Step 4</td>
</tr>
<tr>
<td>1753 instead of 1722</td>
<td>135 answered, the replication interface didn't (Microsoft)</td>
<td>Step 7</td>
</tr>
<tr>
<td>-2146893022 "The target principal name is incorrect"</td>
<td>Kerberos, often a password mismatch (Microsoft)</td>
<td>Step 8</td>
</tr>
<tr>
<td>Last success older than the tombstone lifetime</td>
<td>Not a 1722 problem anymore</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the replication status (read-only)</h2>
<p>Start with a forest-wide summary, then the detail for the destination. Microsoft on <code>/replsummary</code>: it "Identifies domain controllers that are failing inbound replication or outbound replication, and summarizes the results in a report." Run these from an elevated prompt on a DC or a machine with RSAT:</p>
<pre><code class="language-console">:: Read-only. Forest summary by source and destination, worst first (Microsoft's example form)
repadmin /replsummary * /bysrc /bydest /sort:delta

:: Read-only. Inbound status for every DC, only partners with errors
repadmin /showrepl * /errorsonly

:: Read-only. The full detail for the destination DC (BR-DC2 is an example), with the source's CNAME
repadmin /showrepl BR-DC2 /verbose
</code></pre>
<p>Microsoft: <code>/errorsonly</code> "Displays replication status only for source domain controllers with which the destination domain controller encounters replication errors." And <code>/verbose</code> adds the "fully qualified CNAME, invocation ID, replication flags, and update sequence number (USN) values." That CNAME is the name Step 4 resolves.</p>
<p>For a sortable view of the whole forest, Microsoft gives a PowerShell pipe for the CSV output:</p>
<pre><code class="language-powershell"># Read-only. Microsoft's interactive table of forest-wide replication status
repadmin /showrepl * /csv | ConvertFrom-Csv | Out-GridView
</code></pre>
<p>The ActiveDirectory module gives the same data as objects. The property names below come from Microsoft's class reference for each type:</p>
<pre><code class="language-powershell"># Read-only. Current failures for the destination, and its inbound partners (BR-DC2 is an example)
Get-ADReplicationFailure -Target BR-DC2 |
  Select-Object Server, Partner, FailureType, FailureCount, FirstFailureTime, LastError

Get-ADReplicationPartnerMetadata -Target BR-DC2 -PartnerType Inbound |
  Select-Object Server, Partner, Partition, LastReplicationAttempt, LastReplicationSuccess, LastReplicationResult, ConsecutiveReplicationFailures
</code></pre>
<p>One catch before you trust any of it. Microsoft: "The administrative workstation on which you run Repadmin must have remote procedure call (RPC) network connectivity to all domain controllers that are targeted by the DSA_LIST parameter." A 1722 against a DC can be your admin box's path, not the DCs' path. Confirm the status from the destination DC's own console (our reading).</p>
<p>How to read the status code. Each row is from the Microsoft article for that code:</p>
<table>
<thead>
<tr>
<th>Status in <code>/showrepl</code></th>
<th>Microsoft's description</th>
<th>What it tells you</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1722 (0x6ba)</strong> The RPC server is unavailable</td>
<td>"The common case is that the abstract TCP CONNECT operation failed."</td>
<td>No connection: DNS, routing, firewall, NIC, or a service down (Steps 4-7)</td>
</tr>
<tr>
<td><strong>1753 (0x6d9)</strong> There are no more endpoints available from the endpoint mapper</td>
<td>The client "could contact the RPC Server (source DC) over port 135, but the EPM on the RPC Server (source DC) couldn't locate the RPC application of interest"</td>
<td>135 works; the source's AD replication service isn't registered, or DNS sent you to the wrong machine (Step 7)</td>
</tr>
<tr>
<td><strong>1727 (0x6bf)</strong> The remote procedure call failed and did not execute</td>
<td>"More specifically, the replication partner can bind to the RPC endpoint mapper, but can't complete the DRSR RPC bind."</td>
<td>Firewalls, WAN optimizers, or load on the partner (Step 6)</td>
</tr>
<tr>
<td><strong>8524 (0x214c)</strong> DNS lookup failure</td>
<td>"The DSA operation is unable to proceed because of a DNS lookup failure."</td>
<td>Step 4</td>
</tr>
<tr>
<td><strong>-2146893022 (0x80090322)</strong> The target principal name is incorrect</td>
<td>"This error occurs when the source domain controller doesn't decrypt the service ticket provided by the destination (target) domain controller."</td>
<td>Kerberos, often a password mismatch (Step 8)</td>
</tr>
</tbody></table>
<p>Microsoft's 1727 article adds the link back: if port 135 is FILTERED, "the AD replication failure is likely to fail and return error 1722 instead."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>One destination fails from every source</td>
<td>The destination's own DNS, NIC, or firewall</td>
<td>Steps 4, 5</td>
</tr>
<tr>
<td>Every destination fails from one source</td>
<td>The source is down, unreachable, or registered at the wrong IP</td>
<td>Steps 4, 7, 9</td>
</tr>
<tr>
<td>Only intersite partnerships fail</td>
<td>The path between sites</td>
<td>Step 6</td>
</tr>
<tr>
<td>Status flips between 1722 and success</td>
<td>Intermittent network or load. Microsoft lists "Resource limitations" among the causes</td>
<td>Step 6, then a network capture</td>
</tr>
<tr>
<td>Last success is weeks old</td>
<td>Check the tombstone lifetime before anything else</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: Read the Directory Service events (read-only)</h2>
<p>Microsoft's general guidance: "Ideally, the red (Error) and yellow (Warning) events in the Directory Service event log suggest the specific constraint that is causing replication failure on the source or destination domain controller." Read them on the destination:</p>
<pre><code class="language-powershell"># Read-only. Replication-related events on the destination DC, last 3 days (IDs from Microsoft's 1722 and replication articles)
Get-WinEvent -FilterHashtable @{
  LogName   = 'Directory Service'
  Id        = 1311, 1865, 1925, 1960, 2087, 2088, 2042
  StartTime = (Get-Date).AddDays(-3)
} -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, ProviderName, Message | Format-List

# Read-only. LSASRV 40960/40961 (Microsoft ties them to the UDP fragmentation cause; the System log is our note)
Get-WinEvent -FilterHashtable @{ LogName = 'System'; Id = 40960, 40961; StartTime = (Get-Date).AddDays(-3) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, ProviderName, Message
</code></pre>
<table>
<thead>
<tr>
<th>Event</th>
<th>Microsoft's text</th>
<th>Points at</th>
</tr>
</thead>
<tbody><tr>
<td>1925 (NTDS KCC)</td>
<td>"The attempt to establish a replication link for the following writable directory partition failed." The event's Additional Data carries the error value</td>
<td>The error value: 1722, 8524, or another code (Step 2 table)</td>
</tr>
<tr>
<td>1311 (NTDS KCC)</td>
<td>"The Knowledge Consistency Checker (KCC) has detected problems with the following directory partition."</td>
<td>Topology and connectivity. Read alongside 1865</td>
</tr>
<tr>
<td>1865 (NTDS KCC)</td>
<td>"The Knowledge Consistency Checker (KCC) was unable to form a complete spanning tree network topology."</td>
<td>Whole sites unreachable: the path between sites (Step 6)</td>
</tr>
<tr>
<td>1960 (NTDS Replication)</td>
<td>"Internal event: The following domain controller received an exception from a remote procedure call (RPC) connection."</td>
<td>The RPC layer itself (Steps 6, 7)</td>
</tr>
<tr>
<td>2087 (NTDS Replication)</td>
<td>Resolving the source's CNAME, FQDN, and NetBIOS name "to the IP address of the source domain controller have all failed."</td>
<td>Step 4</td>
</tr>
<tr>
<td>2088 (NTDS Replication)</td>
<td>"AD DS couldn't resolve the DNS host name of the source domain controller to an IP address, but replication succeeded."</td>
<td>DNS is broken even though a fallback worked (Step 4)</td>
</tr>
<tr>
<td>2042 (NTDS Replication)</td>
<td>"Replication hasn't occurred with this partner for a tombstone lifetime, and replication can't proceed."</td>
<td>Step 9. Don't force it</td>
</tr>
<tr>
<td>40960 / 40961 (LSASRV)</td>
<td>"Event ID 40960 and 40961 errors with a source of LSASRV are common for this particular cause," UDP fragmentation</td>
<td>The path between the DCs (Step 6)</td>
</tr>
</tbody></table>
<p>Microsoft on fragmentation: "UDP fragmentation can cause replication errors that appear to have a source of RPC server is unavailable". So a 1722 with LSASRV events next to it points at the network path, not at AD (our reading).</p>
<hr />
<h2>Step 4: DNS: can the destination find the source? (read-only)</h2>
<p>Microsoft: "The destination domain controller uses the DNS alias (CNAME) resource record to locate its source domain controller replication partner." That CNAME is <code>&lt;DSA object GUID&gt;._msdcs.&lt;forest root domain&gt;</code>, and it points at the source's host (A) record. Get the GUID from the source's own <code>/showrepl</code> header (the <strong>DSA object GUID</strong> line), then resolve it from the destination against every DNS server the destination uses:</p>
<pre><code class="language-powershell"># Read-only. On the destination DC. GUID, forest root, and DC name are examples; copy the GUID from 'repadmin /showrepl DC1'
$cname = 'f8786828-ecf5-4b7d-ad12-8ab60178f7cd._msdcs.contoso.com'
$dnsServers = (Get-DnsClientServerAddress -AddressFamily IPv4 | Where-Object ServerAddresses).ServerAddresses | Select-Object -Unique
foreach ($dns in $dnsServers) {
  Resolve-DnsName -Name $cname -Server $dns -ErrorAction SilentlyContinue |
    Select-Object @{ n = 'DnsServer'; e = { $dns } }, Name, Type, NameHost, IPAddress
}

# Read-only. The source's A records: more than one IP, or an old IP, is the finding
foreach ($dns in $dnsServers) {
  Resolve-DnsName -Name DC1.contoso.com -Type A -Server $dns -ErrorAction SilentlyContinue |
    Select-Object @{ n = 'DnsServer'; e = { $dns } }, Name, IPAddress
}

# Read-only. A hosts file entry overrides DNS
Select-String -Path "$env:SystemRoot\System32\drivers\etc\hosts" -Pattern 'DC1' -SimpleMatch
</code></pre>
<p>Compare the IP you got with <code>ipconfig /all</code> on the source's console. Microsoft's quick check from the other direction is <code>ping -a</code> with the source's IP: "It's a simple, quick test to validate the host record for a domain controller is resolving to the correct machine." The <code>-Server</code> loop and the hosts-file search are our forms of Microsoft's per-DNS-server <code>nslookup</code> steps.</p>
<p><strong>Microsoft's DNS test.</strong> Run it on both DCs and read the summary table:</p>
<pre><code class="language-console">:: Read-only. DNS health for one DC, verbose, to a file (DC1 is an example; /e instead of /s: tests every DC)
dcdiag /test:dns /v /s:DC1 /f:dcdiag-dns-DC1.txt

:: Read-only. Just the records DCs need: CNAME, A, LDAP SRV, GC SRV, PDC SRV
dcdiag /test:dns /DnsRecordRegistration /s:DC1
</code></pre>
<p>Microsoft notes that without IPv6 "you should expect the host (AAAA) validation portion of the test to fail." Ignore that row if you don't use IPv6.</p>
<p><strong>The DNS client settings on the DCs.</strong> Microsoft's DNS client guidance has three rules that matter for 1722:</p>
<ul>
<li>ISP DNS: "Don't configure the DNS client settings on the domain controllers to point to DNS servers of your Internet Service Provider (ISP)." Netlogon then can't register the records other DCs need.</li>
<li>Islands: for a new DC, Microsoft says "Do not configure the domain controller to utilize its own DNS service for name resolution until you have verified that both inbound and outbound Active Directory replication is functioning and up to date." The same page cites KB 275278, "DNS Server becomes an island when a domain controller points to itself for the _msdcs.ForestDnsName domain".</li>
<li>Wrong answers stick: "Only a failure to respond will cause the DNS client to switch Preferred DNS servers; receiving an authoritative but incorrect response does not cause the DNS client to try another server." So one DNS server with a stale record can break replication while the others have the right one.</li>
</ul>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>CNAME doesn't resolve on one or more DNS servers</td>
<td>Microsoft's 2087 causes include "The source domain controller has not registered its resource records in DNS." and "The destination domain controller is configured to use an invalid DNS server."</td>
<td>Step 10: register records on the source; fix the DNS client settings</td>
</tr>
<tr>
<td>CNAME resolves, A record has an old or extra IP</td>
<td>Stale record. Microsoft: "If invalid IP addresses exist in host records, investigate whether DNS scavenging is enabled and properly configured."</td>
<td>Step 10: fix the record, then Step 5</td>
</tr>
<tr>
<td>Hosts file has an entry for the source</td>
<td>Microsoft's checklist says remove it</td>
<td>Step 10</td>
</tr>
<tr>
<td>DNS servers disagree</td>
<td>Replication latency between DNS servers, or a non-integrated zone (our reading)</td>
<td>Fix the zone first</td>
</tr>
<tr>
<td>A DC points at an ISP or public resolver</td>
<td>Netlogon can't register on that server</td>
<td>Step 10: DNS client settings</td>
</tr>
<tr>
<td>Everything resolves to the right IP</td>
<td>DNS is not the cause</td>
<td>Step 6</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: NICs and DNS registration on both DCs (read-only)</h2>
<p>A DC with two connected adapters, or one adapter with an old secondary IP, can register an address the destination can't reach. Microsoft's DNS client guidance: "If the domain controller that hosts DNS has several network adapters installed, you must disable one adapter for DNS name registration." In a Microsoft Q&amp;A thread about a failing replication link, the accepted answer starts: "Both domain controllers are multi-homed which will always cause no end to grief for active directory DNS." (community answer).</p>
<pre><code class="language-powershell"># Read-only. Run on the source and the destination: adapters, IPs, gateways, DNS servers
Get-NetIPConfiguration -Detailed |
  Select-Object InterfaceAlias, @{ n = 'IPv4'; e = { $_.IPv4Address.IPAddress -join ', ' } },
    @{ n = 'Gateway'; e = { $_.IPv4DefaultGateway.NextHop -join ', ' } },
    @{ n = 'DNS'; e = { $_.DNSServer.ServerAddresses -join ', ' } }

# Read-only. Which adapters register their address in DNS
Get-DnsClient | Select-Object InterfaceAlias, ConnectionSpecificSuffix, RegisterThisConnectionsAddress
</code></pre>
<p><code>RegisterThisConnectionsAddress</code> "Indicates whether the IP address for this connection is to be registered." Then compare what you see with Step 4's A records.</p>
<p>Microsoft's 1722 article lists the rest of the NIC layer in two lines: "Bad NIC drivers" and "See network card vendors or OEMs for the latest drivers." Check the driver version against the vendor's current release before you blame AD (our reading). Its cause list also starts with "Link local failure" and "DHCP failure," so a DC that picked up an APIPA or DHCP address after a NIC change belongs here too (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Two adapters registering in DNS</td>
<td>The source registers an IP the destination may not route to</td>
<td>Step 10: stop registration on one adapter</td>
</tr>
<tr>
<td>A backup, iSCSI, or management adapter with a gateway</td>
<td>Traffic to the partner may leave through the wrong adapter</td>
<td>The network team; one default gateway</td>
</tr>
<tr>
<td>DNS servers differ between the two DCs</td>
<td>Each DC may see a different answer (Step 4)</td>
<td>Align them to internal DNS</td>
</tr>
<tr>
<td>169.254.x.x or an unexpected DHCP address</td>
<td>Microsoft's "Link local failure" and "DHCP failure" causes</td>
<td>Fix the adapter's IP configuration</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: Port 135, the RPC dynamic range, and the firewalls (read-only)</h2>
<p>AD replication is a two-step RPC conversation. Microsoft: "Active Directory replication remote procedure calls (RPCs) occur dynamically over an available port through the RPC Endpoint Mapper (RPCSS) on port 135." The destination asks the source's endpoint mapper on 135 which port the replication interface is on, then connects to that port. Microsoft on current Windows: "The new default start port is 49152, and the new default end port is 65535." Firewalls between DCs have to allow both steps.</p>
<p>The DC-to-DC rows that matter here come from Microsoft's firewall article for Active Directory (Windows Server 2008 and later). Microsoft notes that "Not all the ports that are listed in the tables here are required in all scenarios." The rows:</p>
<table>
<thead>
<tr>
<th>Server port</th>
<th>Service (Microsoft's table)</th>
<th>Why replication cares (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>135/TCP</td>
<td>RPC Endpoint Mapper</td>
<td>Step one of every replication bind</td>
</tr>
<tr>
<td>49152-65535/TCP</td>
<td>RPC for LSA, SAM, NetLogon</td>
<td>Step two. AD replication's port is dynamic in this range unless pinned (Microsoft's 1753 article)</td>
</tr>
<tr>
<td>53/TCP/UDP</td>
<td>DNS</td>
<td>Step 4's lookups</td>
</tr>
<tr>
<td>88/TCP/UDP</td>
<td>Kerberos</td>
<td>Authentication for the bind (Step 8)</td>
</tr>
<tr>
<td>389/TCP/UDP</td>
<td>LDAP</td>
<td>DC locator and directory reads</td>
</tr>
<tr>
<td>3268/TCP</td>
<td>LDAP GC</td>
<td>Global catalog lookups</td>
</tr>
<tr>
<td>445/TCP</td>
<td>SMB</td>
<td>Named pipes and SYSVOL</td>
</tr>
<tr>
<td>464/TCP/UDP</td>
<td>Kerberos password change</td>
<td>Machine password changes</td>
</tr>
<tr>
<td>123/UDP</td>
<td>W32Time</td>
<td>Time within Kerberos tolerance</td>
</tr>
</tbody></table>
<p>Microsoft's guidance on the range: "Therefore, you must increase the RPC port range in your firewalls." And: "we recommend that you reconfigure the firewalls to allow traffic between servers in the dynamic port range of 49152 through 65535."</p>
<p><strong>Test the two steps from the destination.</strong> Microsoft's 1722 and 1727 articles use PortQry, a free Microsoft download. First the endpoint mapper, then the port the replication interface registered:</p>
<pre><code class="language-console">:: Read-only. From the destination DC: query the source's endpoint mapper and save the list (DC1 is an example)
portqry -n DC1.contoso.com -e 135 &gt; portqry-DC1-135.txt

:: Read-only. Then test the port listed for "MS NT Directory DRS Interface" with ncacn_ip_tcp (49156 is an example)
portqry -n DC1.contoso.com -e 49156
</code></pre>
<p>In the saved file, find the <code>MS NT Directory DRS Interface</code> entries (UUID <code>e3514235-4b06-11d1-ab04-00c04fc2dcd2</code>) and note the <code>ncacn_ip_tcp</code> port. PortQry's three answers, per Microsoft: "LISTENING: This response indicates that a process is listening on the target port." "NOT LISTENING: This response indicates that no process is listening on the target port." "FILTERED: This response indicates that the target port is being filtered."</p>
<p><code>Test-NetConnection</code> gives the same TCP answer for a list of ports, without a download. It "Specifies the TCP port number on the remote computer," so it proves a TCP connect, not an RPC bind:</p>
<pre><code class="language-powershell"># Read-only. TCP from the destination to the source (ports from Microsoft's table; 49156 = the DRS port PortQry showed, an example)
foreach ($port in 135, 49156, 389, 445, 88, 53, 3268) {
  Test-NetConnection -ComputerName DC1.contoso.com -Port $port -WarningAction SilentlyContinue |
    Select-Object ComputerName, RemoteAddress, RemotePort, TcpTestSucceeded
}
</code></pre>
<p>Check <code>RemoteAddress</code> against Step 4. A successful test to the wrong IP is still the wrong DC (our note).</p>
<p><strong>Has anyone changed the range or pinned the ports?</strong> On the source DC, read the dynamic range and the registry values Microsoft documents for restricting it:</p>
<pre><code class="language-console">:: Read-only. The source DC's dynamic port range (Microsoft's netsh form)
netsh int ipv4 show dynamicport tcp
</code></pre>
<pre><code class="language-powershell"># Read-only. On the source DC: restricted RPC range, pinned AD and Netlogon ports, firewall profiles (missing values = not set)
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Rpc\Internet' -ErrorAction SilentlyContinue |
  Select-Object Ports, PortsInternetAvailable, UseInternetPorts
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name 'TCP/IP Port' -ErrorAction SilentlyContinue
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -Name DCTcpipPort -ErrorAction SilentlyContinue
Get-NetFirewallProfile | Select-Object Name, Enabled
</code></pre>
<p>Microsoft on the <code>Rpc\Internet</code> key: "The values (and Internet key) discussed below don't appear in the registry. They must be added manually using the Registry Editor." So if the key exists, someone added it. And: "If any entries are outside the range of 0 to 65535, or if any string can't be interpreted, the RPC runtime treats the entire configuration as invalid."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>135 is FILTERED</td>
<td>A firewall or route blocks the endpoint mapper. The 1727 article: replication then fails with 1722</td>
<td>Firewall team: 135 between the DCs</td>
</tr>
<tr>
<td>135 LISTENING, the DRS port FILTERED</td>
<td>The firewall allows 135 but not the dynamic range (our reading)</td>
<td>Step 10: open 49152-65535 TCP between DCs, or pin the port</td>
</tr>
<tr>
<td>135 LISTENING, no <code>MS NT Directory DRS Interface</code> in the list</td>
<td>The replication interface isn't registered: 1753 territory</td>
<td>Step 7</td>
</tr>
<tr>
<td>Every TCP test succeeds, replication still fails</td>
<td>Name resolution to a different IP (Step 4), an intermediate device that breaks the bind (1727), or something intermittent</td>
<td>Microsoft's 1727 step: "Take a double-sided network capture while you reproduce the problem."</td>
</tr>
<tr>
<td><code>Rpc\Internet</code> exists with a narrow range</td>
<td>Restricted RPC. Microsoft: "Computers with higher traffic may run into a port exhaustion situation if the RPC dynamic ports are restricted."</td>
<td>Widen it or remove it deliberately (Step 10)</td>
</tr>
<tr>
<td><code>TCP/IP Port</code> set, firewall opened only for the default range</td>
<td>Microsoft: "Administrators should confirm that the communication over the specified port is enabled if any intermediate network devices or software is used to filter packets between the domain controllers."</td>
<td>Open the pinned port too</td>
</tr>
<tr>
<td>Netlogon event 5820 on the source</td>
<td>Microsoft: an RPC port configuration error returns "87 (0x57) ERROR_INVALID_PARAMETER" and Netlogon logs 5820</td>
<td>Fix the <code>Rpc\Internet</code> values</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: The RPC server side: the source DC itself (read-only)</h2>
<p>Two of Microsoft's 1722 causes are on the source: "Higher-layer protocol not running" and "Higher-layer protocol returning this error." Read the source's own state from its console:</p>
<pre><code class="language-console">:: Read-only. From the destination: does the source answer as a DC? (Microsoft's net view check)
net view \\DC1
</code></pre>
<p>Microsoft: "This command displays the Netlogon and SYSVOL shares, indicating that the server is functioning as a domain controller."</p>
<pre><code class="language-powershell"># Read-only. On the source DC: the services replication depends on (service names are our note)
Get-Service -Name NTDS, RpcSs, RpcEptMapper, Netlogon, KDC, DNS -ErrorAction SilentlyContinue |
  Format-Table Name, DisplayName, Status, StartType

# Read-only. Something should be listening on 135
Get-NetTCPConnection -State Listen -LocalPort 135 | Select-Object LocalAddress, LocalPort, OwningProcess

# Read-only. Microsoft's ClientProtocols check (run on both DCs): ncacn_http, ncacn_ip_tcp, ncacn_np, ncacn_ip_udp = rpcrt4.dll
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Rpc\ClientProtocols'
</code></pre>
<p>Microsoft's 1722 article: "If the ClientProtocols key or any of the four default values are missing, import the key from a known good server." Its 1753 article adds the boot state: "Verify that the source DC is booted in normal mode. And verify that the OS and DC role on the source DC have fully started."</p>
<p><strong>SMB signing.</strong> Microsoft's 1722 article lists "SMB signing mismatches between DCs" as a cause and recommends consistent settings through the Default Domain Controllers Policy. Read the values on both DCs:</p>
<pre><code class="language-powershell"># Read-only. SMB client and server signing values (1 = enabled; names from Microsoft's 1722 article)
foreach ($svc in 'LanManWorkstation', 'LanManServer') {
  Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$svc\Parameters" |
    Select-Object @{ n = 'Service'; e = { $svc } }, RequireSecuritySignature, EnableSecuritySignature
}
</code></pre>
<p>The sample values in that article turn "always" signing off. We wouldn't weaken signing on DCs to fix a mismatch. Make the values match through policy at the stricter setting your baseline uses (our view).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>net view \\DC1</code> fails, ping works</td>
<td>The source is up but not answering as a DC, or SMB is blocked</td>
<td>The services above; Step 6 for 445</td>
</tr>
<tr>
<td><code>NTDS</code> stopped or the DC booted into DSRM</td>
<td>Microsoft's 1753 causes: the server application never started or died</td>
<td>Start it after reading why it stopped (System and Directory Service logs)</td>
</tr>
<tr>
<td>Nothing listening on 135</td>
<td>RPC itself is down</td>
<td><code>RpcSs</code> and <code>RpcEptMapper</code></td>
</tr>
<tr>
<td><code>ClientProtocols</code> values missing</td>
<td>Microsoft: import the key from a known good server</td>
<td>Step 10</td>
</tr>
<tr>
<td>Signing values differ between DCs</td>
<td>Microsoft's "SMB signing mismatches" cause</td>
<td>Align by policy (Step 10)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: The sibling: "The target principal name is incorrect" (read-only)</h2>
<p>If <code>/showrepl</code> shows -2146893022 (0x80090322) instead of 1722, the network got through and Kerberos failed. Microsoft: "This error occurs when the source domain controller doesn't decrypt the service ticket provided by the destination (target) domain controller." Its top cause: "The destination domain controller receives a service ticket from a Kerberos Key Distribution Center (KDC). And the KDC has an old version of the password for the source domain controller." Microsoft's specific root causes:</p>
<ul>
<li>"A bad name-to-IP mapping in DNS, WINS, HOST, or LMHOST file." The destination reached the wrong DC (Step 4 again).</li>
<li>"The KDC and source domain controller have different versions of the source domain controller's computer account password."</li>
<li>"The KDC couldn't find a domain to look for the source domain controller's SPN."</li>
<li>"Authentication data in Kerberos encrypted frames were modified by hardware (including network devices), software, or an attacker."</li>
</ul>
<p>Microsoft's reads, in order:</p>
<pre><code class="language-console">:: Read-only. On the source DC's console: missing or duplicate SPNs and other security errors
dcdiag /test:checksecurityerror

:: Read-only. On the destination, close to the time of the error: which KDC is it using?
nltest /dsgetdc:contoso.com /kdc /force

:: Read-only. Password attribute versions for the source's computer account: on the source, then on that KDC (DC3 is an example)
repadmin /showobjmeta DC1 "CN=DC1,OU=Domain Controllers,DC=contoso,DC=com"
repadmin /showobjmeta DC3 "CN=DC1,OU=Domain Controllers,DC=contoso,DC=com"
</code></pre>
<p>Microsoft: "DCDIAG /TEST:CheckSecurityError can check for missing or duplicate SPNs and other errors." Run it "on the console of all source domain controllers that fail outbound replication with the SEC_E_WRONG_PRINCIPAL error." For the <code>/showobjmeta</code> pair, Microsoft says to look "for obvious version number differences in password-related attributes (dBCSPwd, UnicodePWD, NtPwdHistory, PwdLastSet, lmPwdHistory)." And on <code>nltest</code>: "/FORCE: Forces the computer to run the command against the DNS server instead of looking in the cache for the information."</p>
<p><strong>Time.</strong> Microsoft lists "Time accuracy within 5 minutes to support Kerberos authentication" among replication's dependencies. A quick read of the offset between the two DCs:</p>
<pre><code class="language-console">:: Read-only. Three samples of the offset from the destination to the source (w32tm syntax from Microsoft's Windows Time tools page)
w32tm /stripchart /computer:DC1.contoso.com /samples:3 /dataonly
</code></pre>
<p>If the offset is minutes, not milliseconds, read <a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing with Domain Controller</a> before you touch passwords.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>CNAME or A record points at another DC or another forest</td>
<td>Bad name-to-IP mapping; the realm in the ticket is wrong</td>
<td>Step 4, then Step 10</td>
</tr>
<tr>
<td><code>checksecurityerror</code> reports missing or duplicate SPNs</td>
<td>Microsoft: "Duplicate SPNs may cause bad SPN to name mappings."</td>
<td>Fix the SPN (our reading: find the duplicate owner first)</td>
</tr>
<tr>
<td>Password attribute versions differ between the source and the KDC</td>
<td>The KDC has the old password: Microsoft's top cause</td>
<td>Step 10: Microsoft's KDC sequence</td>
</tr>
<tr>
<td>Offset of minutes between the DCs</td>
<td>Outside Kerberos tolerance</td>
<td>The Windows Time post</td>
</tr>
<tr>
<td>Error right after a DC restore or a long outage</td>
<td>Replication failures "can prevent domain controllers from having a current password value for domain controllers in a given domain"</td>
<td>Step 10: KDC sequence or <code>netdom resetpwd</code></td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Is the partner still a real, current DC? (read-only)</h2>
<p>Microsoft's first check before any 1722 fix: rule out intentional disconnections. Its example is a DC "that has been built in a staging site and is currently offline awaiting its deployment in the final production site." Also check whether the source still exists at all. Microsoft's 2087 event text: "If the source domain controller is no longer functioning or its operating system has been reinstalled with a different computer name or NTDSDSA object GUID, remove the source domain controller's metadata with ntdsutil.exe".</p>
<p><strong>The tombstone lifetime.</strong> If the last success is older than the forest's tombstone lifetime, a 1722 fix that lets replication resume can bring lingering objects with it (our reading; Microsoft's event 1388 is a lingering object replicating when strict replication consistency isn't in effect). Microsoft's <code>/showrepl</code> message for that case: "The time since last replication with this server has exceeded the tombstone lifetime." Read the value:</p>
<pre><code class="language-powershell"># Read-only. The forest's tombstoneLifetime in days (the object path and read form are ours)
$config = (Get-ADRootDSE).configurationNamingContext
Get-ADObject -Identity "CN=Directory Service,CN=Windows NT,CN=Services,$config" -Properties tombstoneLifetime |
  Select-Object tombstoneLifetime
</code></pre>
<p>Microsoft: "In any forest created using Windows Server 2003 SP1 or later, the value of tombstoneLifetime is set to 180 days by default. In any forest upgraded from Windows 2000 or installed with Windows Server 2003 (no service pack), the default tombstoneLifetime attribute is not set and Windows therefore uses the internal default of 60 days."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The source was decommissioned or rebuilt</td>
<td>Its NTDS Settings object is stale; replication keeps trying it</td>
<td>Metadata cleanup, deliberately, after you confirm it's gone</td>
</tr>
<tr>
<td>The source is a staged DC, offline on purpose</td>
<td>Expected errors</td>
<td>Document it; Microsoft suggests IFM for staged DCs</td>
</tr>
<tr>
<td>Last success older than the tombstone lifetime</td>
<td>Event 2042 territory: "Replication hasn't occurred with this partner for a tombstone lifetime, and replication can't proceed."</td>
<td>Don't force it. Microsoft's lingering-object guidance first</td>
</tr>
<tr>
<td>Last success within days, the source is real</td>
<td>A 1722 fix is safe to test</td>
<td>Step 10</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Restricting the RPC port range first</strong></td>
<td>Microsoft: "Use the method that is described in this article only if the RPC server does not offer a way to define the server port." A narrow range can exhaust ports, and a bad value makes "the entire configuration" invalid</td>
<td>Find the blocked step with PortQry (Step 6)</td>
</tr>
<tr>
<td><strong><code>repadmin /replicate ... /force</code></strong></td>
<td>Microsoft: <code>/force</code> "allows replication with corrupt and divergent partners. Do not use this parameter if you suspect that Update Sequence Number (USN) Rollback has occurred."</td>
<td>Read the last success and the tombstone lifetime (Step 9)</td>
</tr>
<tr>
<td><strong>Repeating <code>repadmin /syncall /AdeP</code></strong></td>
<td>It asks for replication again; it doesn't change the path that failed (our reading)</td>
<td>One <code>/replicate</code> after one fix (Step 10)</td>
</tr>
<tr>
<td><strong>Demoting and re-promoting the DC</strong></td>
<td>Microsoft lists rebuilding only "If the problem that is causing replication to fail can't be resolved by any known methods"</td>
<td>Steps 4-8 name the layer first</td>
</tr>
<tr>
<td><strong>Turning off Windows Firewall on the DCs</strong></td>
<td>It hides which rule was missing, and the next DC build fails the same way (our view)</td>
<td>Open the documented ports between DCs (Step 10)</td>
</tr>
<tr>
<td><strong>Adding hosts file entries for the partner</strong></td>
<td>Microsoft lists stale hosts entries as a cause of wrong-target errors. They outlive the next re-IP</td>
<td>Fix the DNS record (Step 4)</td>
</tr>
<tr>
<td><strong><code>netdom resetpwd</code> for a plain 1722</strong></td>
<td>Microsoft's netdom sequence is for password mismatches (-2146893022); 1722 means no connection was made (our reading). A Microsoft Q&amp;A answer suggests it for 1722 anyway (community)</td>
<td>Steps 4-7 for 1722; Step 8 for the target principal error</td>
</tr>
<tr>
<td><strong>Metadata cleanup on a DC that's only unreachable</strong></td>
<td>It deletes a working DC's objects from AD (our reading of Microsoft's cleanup steps)</td>
<td>Confirm the DC is gone (Step 9)</td>
</tr>
<tr>
<td><strong>Turning off SMB signing to make DCs match</strong></td>
<td>Fixes a mismatch by weakening both DCs (our view)</td>
<td>Match by policy at the stricter setting</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one layer, then prove it (only after the read)</h2>
<p>Pick the one change Steps 1-9 point at. Save the current state first, so you can compare or put it back:</p>
<pre><code class="language-powershell"># Low-risk write. Save replication status and the registry keys this step may touch (our habit)
New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null
repadmin /showrepl * /csv &gt; C:\Temp\showrepl-before.csv
reg export 'HKLM\SOFTWARE\Microsoft\Rpc' C:\Temp\rpc-before.reg /y
reg export 'HKLM\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' C:\Temp\ntds-params-before.reg /y
reg export 'HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' C:\Temp\netlogon-params-before.reg /y
</code></pre>
<p><strong>DNS records (Step 4).</strong> Fix a stale A record in the DNS console, remove any hosts file entry for the partner, then have the source register again. Microsoft: "The Net Logon service on a domain controller registers the DNS resource records that are required for the domain controller to be located on the network. The DNS Client service registers the host (A) resource record that the alias (CNAME) record points to." Its commands, on the source DC:</p>
<pre><code class="language-console">:: Change, on the source DC. Microsoft's manual registration: Netlogon records, then the host (A) record
net stop netlogon &amp;&amp; net start netlogon
ipconfig /flushdns &amp;&amp; ipconfig /registerdns
</code></pre>
<p>Microsoft: "Wait 15 minutes, and then review events in Event Viewer to ensure proper registration of the resource records." Then run <code>ipconfig /flushdns</code> on the destination and resolve the CNAME again (Step 4).</p>
<p><strong>An extra adapter registering (Step 5).</strong> Turn off registration on the adapter that shouldn't be in DNS, then remove its stale A record:</p>
<pre><code class="language-powershell"># Change. Dry-run first ('Backup' is an example adapter name), then re-register the remaining adapter
Set-DnsClient -InterfaceAlias 'Backup' -RegisterThisConnectionsAddress $false -WhatIf
Register-DnsClient -WhatIf
</code></pre>
<p><strong>DC DNS client settings (Step 4).</strong> Point each DC at internal DNS servers only; send internet names through forwarders. Microsoft: "To forward external DNS requests, add the ISP's DNS servers as DNS forwarders in the DNS management console." After the change, Microsoft's order: "If you change any DNS client settings, you must clear the DNS resolver cache and register the DNS resource records."</p>
<p><strong>The firewall (Step 6).</strong> Ask for a rule scoped to DC-to-DC traffic: 135/TCP and 49152-65535/TCP, plus the other ports in Microsoft's table, in both directions, because each DC is a destination for some partners (our reading). Test with PortQry from both sides afterward. Microsoft's 1727 steps end the same way: "Repeat step 1 through 3 on the source DC."</p>
<p><strong>Pinning AD's RPC port (Step 6), if the firewall team needs one port.</strong> Microsoft's KB 224196 values, on each DC that should use them. Microsoft: "Don't assume that clients only use the Netlogon RPC services and thus only the setting DCTcpipPort is required." And: "You should always configure both registry settings and open both ports on the firewall." Two different ports; the numbers below are examples:</p>
<pre><code class="language-powershell"># Change. Pin NTDS and Netlogon to two different ports (50000/50001 are examples). Dry-run first
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NTDS\Parameters' -Name 'TCP/IP Port' -PropertyType DWord -Value 50000 -WhatIf
New-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -Name DCTcpipPort -PropertyType DWord -Value 50001 -WhatIf
</code></pre>
<p>Microsoft: for <code>TCP/IP Port</code>, "Restart the computer for the new setting to become effective." For <code>DCTcpipPort</code>, "Restart the Netlogon service for the new setting to become effective." If both values are the same port, "Netlogon error event 5809 is logged in the system log." Microsoft's known-issues list for static ports names logon and replication problems that later updates fix, so patch first (our summary of that list).</p>
<p><strong>Restricting the whole RPC range (Step 6), last.</strong> Only when the firewall can't allow the default range and no per-service port exists. Microsoft's example range "isn't a recommendation of a minimum number of ports needed for any particular system," and "previous experience shows that a minimum of 100 ports should be opened, because several system services rely on these RPC ports to communicate with each other." The values go under <code>HKLM\Software\Microsoft\Rpc\Internet</code> (<code>Ports</code>, <code>PortsInternetAvailable</code>, <code>UseInternetPorts</code>), then "Restart the server." Test one DC first, and keep the <code>rpc-before.reg</code> export to put it back (our habit).</p>
<p><strong>ClientProtocols (Step 7).</strong> If the read found values missing, Microsoft's fix is to "import the key from a known good server." Export it on a healthy DC of the same version and import it with <code>reg import</code> on the broken one (the export and import form is ours).</p>
<p><strong>SMB signing (Step 7).</strong> Set the four signing policies once, in the Default Domain Controllers Policy, so both DCs match. Pick the stricter value your baseline uses (our view).</p>
<p><strong>The target principal name is incorrect (Step 8).</strong> Microsoft's top resolution, on the destination DC: disable the KDC, restart, replicate from the source, then put the KDC back:</p>
<pre><code class="language-powershell"># Change, on the destination DC (BR-DC2). Microsoft's sequence; dry-run the startup change first
Set-Service -Name KDC -StartupType Disabled -WhatIf
# ...restart the destination DC, then replicate from the source (Microsoft's repadmin form):
#    repadmin /replicate BR-DC2.contoso.com DC1.contoso.com "DC=contoso,DC=com"
Set-Service -Name KDC -StartupType Automatic -WhatIf   # Microsoft's article writes Auto; Set-Service documents Automatic
Start-Service -Name KDC -WhatIf
</code></pre>
<p>Microsoft's fallback: "If it doesn't resolve the issue, see the Resolution section for an alternative solution in which you use the netdom resetpwd command to reset the computer account password of the source domain controller." Run it on the source DC itself. Microsoft: "If you want to reset the password for a Windows domain controller, you must stop the Kerberos Key Distribution Center service and set its startup type to Manual." Then clear the ticket cache, run the reset against a healthy DC, and restart:</p>
<pre><code class="language-console">:: Change, on the source DC (DC1) with its KDC stopped and set to Manual. DC3 is a healthy DC, an example
klist purge -li 0x3e7
netdom resetpwd /s:DC3 /ud:contoso\admin.user /pd:*
</code></pre>
<p>Microsoft: "Kerberos tickets for the system account that are used by Active Directory replication can be purged without a restart by using KLIST -li 0x3e7 purge." After the restart, start the KDC and set it back to Automatic.</p>
<p><strong>More detail if you're still stuck.</strong> Microsoft's 2087 event text: "By default, only up to 10 DNS failures are shown for any given 12 hour period, even if more than 10 failures occur." Its target-principal article sets <code>DS RPC Client</code> under <code>NTDS\Diagnostics</code> to 3 and then looks for events 1962 and 1963. Turn it up for the test, and back to 0 afterward (the reset to 0 is our habit).</p>
<p><strong>Then prove it.</strong> One destination, one source, one partition, then the same reads as Step 2. Microsoft: "The /replicate command tests replication success after you remove suspected fault conditions without waiting for the replication schedule to open."</p>
<pre><code class="language-console">:: Read-mostly. Trigger one inbound replication, then re-read status (names and partition are examples)
repadmin /replicate BR-DC2 DC1 DC=contoso,DC=com
repadmin /showrepl BR-DC2
repadmin /replsummary * /bysrc /bydest /sort:delta
</code></pre>
<pre><code class="language-powershell"># Read-only. No current failures for the destination, and a fresh last success from the source
Get-ADReplicationFailure -Target BR-DC2 | Select-Object Partner, FailureCount, LastError
Get-ADReplicationPartnerMetadata -Target BR-DC2 -PartnerType Inbound |
  Select-Object Partner, Partition, LastReplicationSuccess, LastReplicationResult
</code></pre>
<p>Check the other partitions on the next scheduled cycle, and check the reverse direction: the source pulling from the destination. Then confirm the change the user waited for, like the group membership on the branch DC (our habit).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Stale or missing DNS records</td>
<td>Fix the record; Microsoft's Netlogon restart and <code>ipconfig /registerdns</code> on the source</td>
<td>Hosts file entries</td>
</tr>
<tr>
<td>Multihomed DC registering the wrong IP</td>
<td>Stop registration on the extra adapter</td>
<td>Leaving both and hoping the right one answers</td>
</tr>
<tr>
<td>DCs pointing at ISP DNS</td>
<td>Internal DNS on the DCs, forwarders for the internet</td>
<td>Public resolvers on DC adapters</td>
</tr>
<tr>
<td>Firewall blocks 135 or the dynamic range</td>
<td>A DC-to-DC rule for 135 and 49152-65535</td>
<td>Turning off Windows Firewall</td>
</tr>
<tr>
<td>Firewall team needs one port</td>
<td>Pin <code>TCP/IP Port</code> and <code>DCTcpipPort</code> to two ports, open both</td>
<td>Pinning one and opening one</td>
</tr>
<tr>
<td>No other option for RPC</td>
<td>A restricted range with room to spare, tested on one DC</td>
<td>A tiny range on busy DCs</td>
</tr>
<tr>
<td>-2146893022 password mismatch</td>
<td>Microsoft's KDC sequence, then <code>netdom resetpwd</code> if needed</td>
<td>Rebuilding the DC</td>
</tr>
<tr>
<td>Last success past the tombstone lifetime</td>
<td>Lingering-object guidance first</td>
<td><code>/force</code> and hope</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does Active Directory replication error 1722 mean?</strong>
The destination DC's RPC client couldn't connect to the RPC server on the source DC. Microsoft: "RPC error 1722/0x6ba/RPC_S_SERVER_UNAVAILABLE is logged when a lower-layer protocol reports a connectivity failure." The usual lower layers are DNS, routing and firewalls, the NIC, or a service that isn't running on the source.</p>
<p><strong>How do I fix "The RPC server is unavailable" between domain controllers?</strong>
Read before you change. Get the exact partner and status from <code>repadmin /showrepl</code>, resolve the source's GUID-based CNAME from the destination against each of its DNS servers, then test port 135 and the replication port with PortQry. Fix the one layer that fails, and prove it with <code>repadmin /replicate</code> for one partition.</p>
<p><strong>What ports does Active Directory replication need?</strong>
135/TCP for the RPC endpoint mapper and the dynamic range 49152-65535/TCP on current Windows, unless you've pinned AD's port. DNS (53), Kerberos (88), LDAP (389), and SMB (445) are part of the same conversation. Microsoft's firewall article for AD domains and trusts has the full table.</p>
<p><strong>What's the difference between 1722 and "The target principal name is incorrect"?</strong>
1722 means no connection was made. -2146893022, "The target principal name is incorrect," means the connection was made but the source couldn't decrypt the destination's Kerberos ticket. Microsoft's top cause is a KDC with an old copy of the source DC's password, or DNS sending the destination to the wrong DC.</p>
<p><strong>Does <code>repadmin /syncall /AdeP</code> fix replication errors?</strong>
No. It asks for replication: Microsoft's flags are <code>/A</code> for all naming contexts, <code>/d</code> for distinguished names in messages, <code>/e</code> for all sites, and <code>/P</code> to push changes outward. If the path is broken, it fails with the same error. Use it to confirm a fix, not to make one.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage-3j2a">Netlogon Event ID 5719</a> (the member side: DC locator, DNS, ports, and sites at boot)</li>
<li><a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication Not Working</a> (when SYSVOL or replicated folders are the ones behind; DFSR has its own 1722 searches)</li>
<li><a href="https://dev.to/adminpackstudio/dfs-namespace-not-accessible-a-read-first-referral-root-target-site-and-folder-target-triage-ia3">DFS Namespace Not Accessible</a> (namespace servers read from the PDC emulator and AD sites, so AD replication matters there too)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing with Domain Controller</a> (Kerberos needs DCs within five minutes of each other)</li>
</ul>
<h2>Free checklist</h2>
<p>A firewall change that allowed port 135 between sites but not the RPC range is a change nobody tested from where the replication flows. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=active-directory-replication-error-1722">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=active-directory-replication-error-1722</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=active-directory-replication-error-1722">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=active-directory-replication-error-1722</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>repadmin /replsummary</code>, <code>/showrepl</code>, <code>/showobjmeta</code>, <code>Get-ADReplicationFailure</code>, <code>Get-ADReplicationPartnerMetadata</code>, <code>Get-WinEvent</code>, <code>Get-DnsClientServerAddress</code>, <code>Resolve-DnsName</code>, <code>Select-String</code>, <code>dcdiag /test:dns</code>, <code>dcdiag /test:checksecurityerror</code>, <code>Get-NetIPConfiguration</code>, <code>Get-DnsClient</code>, <code>portqry</code>, <code>Test-NetConnection</code>, <code>netsh int ipv4 show dynamicport</code>, <code>Get-ItemProperty</code>, <code>Get-NetFirewallProfile</code>, <code>net view</code>, <code>Get-Service</code>, <code>Get-NetTCPConnection</code>, <code>nltest /dsgetdc</code>, <code>w32tm /stripchart</code>, <code>Get-ADRootDSE</code>, and <code>Get-ADObject</code>. <code>repadmin /showrepl /csv</code> redirection and <code>reg export</code> write local files. DNS record fixes, Netlogon restarts, <code>ipconfig /registerdns</code>, <code>Set-DnsClient</code>, <code>Register-DnsClient</code>, DNS client and forwarder changes, firewall rules, <code>TCP/IP Port</code>, <code>DCTcpipPort</code>, <code>Rpc\Internet</code> values, <code>ClientProtocols</code> imports, SMB signing policy, KDC startup changes, <code>klist purge</code>, <code>netdom resetpwd</code>, diagnostic logging, <code>repadmin /replicate</code>, and metadata cleanup are deliberate changes. Change one layer at a time, test with one partnership first, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[WSUS Not Downloading Updates: A Read-First Sync, Content Folder, BITS, Proxy, and TLS Triage]]></title><description><![CDATA[WSUS not downloading updates? Read the sync result, update file settings, content folder, BITS, proxy, and TLS path before you rebuild it.]]></description><link>https://adminpackstudio.hashnode.dev/wsus-not-downloading-updates-a-read-first-sync-content-folder-bits-proxy-and-tls-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/wsus-not-downloading-updates-a-read-first-sync-content-folder-bits-proxy-and-tls-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 10:41:16 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Patch Tuesday was Tuesday. It's Friday, and the WSUS console still says 47 updates are needing files. The Windows 11 cumulative update is approved for the Pilot group, and the pilot PCs sit at 'Downloading - 0%'. Last night's synchronization shows Failed, and the Synchronizations page doesn't say why. A forum thread says to stop WSUS, delete the WsusContent folder, run wsusutil reset, and reinstall the role if that doesn't work. Can we do that before tonight's change window?"</p>
<p>Not yet. <strong>"WSUS not downloading updates" is two pipelines that fail separately: synchronization brings the update metadata, and the content download brings the files. Each one has its own log lines and its own fixes.</strong> Microsoft's WSUS API documentation: "WSUS downloads the update files and required license agreements in parallel with synchronizing the update metadata." Deleting WsusContent throws away every file that did download and makes the server fetch it all again through the same broken path. Work through it as a chain: <strong>the console state and what changed</strong> → <strong>the server's view: last sync result, updates needing files, download progress</strong> → <strong>the update file settings</strong> → <strong><code>SoftwareDistribution.log</code> and event 364</strong> → <strong>the path to Microsoft Update or the upstream server: endpoint, ports, proxy, inspection</strong> → <strong>TLS 1.2 and ciphers</strong> → <strong>the content folder: space, <code>ContentDir</code>, permissions</strong> → <strong>BITS on the server</strong> → <strong>Windows 11 UUP MIME types and the client's download from <code>Content</code></strong> → <strong>disconnected servers fed by export and import</strong>. <code>Get-WsusServer</code>, the WSUS administration API, <code>SoftwareDistribution.log</code>, <code>Get-WinEvent</code>, <code>Test-NetConnection</code>, <code>Get-Acl</code>, and <code>Get-BitsTransfer</code> are the sources for that chain. Reinstall the role behind a proxy that strips Range headers, and the new server stops at the same file.</p>
<p>If the files are on the server and the problem is that clients never show up, never scan, or never report, read <a href="https://dev.to/adminpackstudio/wsus-clients-not-reporting-a-read-first-console-client-policy-proxy-and-susclientid-triage-54p7">WSUS Clients Not Reporting</a> first. It covers client policy, the scan source, the client proxy, <code>WsusPool</code>, and duplicate <code>SusClientId</code> values, so this post doesn't repeat them. This post covers <strong>"the WSUS server won't synchronize, or it synchronizes but approved updates stay at 'needing files', downloads sit at 0, event 364 says content file download failed, or Windows 11 updates never finish, and you need a read-first path before you delete WsusContent, run wsusutil reset, or rebuild WSUS."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Sync and download are separate.</strong> A sync can succeed while files fail, and the other way round. Read <code>GetLastSynchronizationInfo()</code> and <code>GetStatus().UpdatesNeedingFilesCount</code> before you pick a layer.</li>
<li><strong>The console may hide the sync error.</strong> Microsoft's known issue: "After installing KB5070881 or later updates, Windows Server Update Services (WSUS) does not display synchronization error details within its error reporting." Read <code>SoftwareDistribution.log</code> instead.</li>
<li><strong>Event 364 names the reason.</strong> "Content file download failed. Reason:" is followed by an HTTP status or a BITS message, and each one points at a different layer (Step 4).</li>
<li><strong>The endpoint is TLS 1.2 only.</strong> Microsoft: <code>https://sws.update.microsoft.com</code> "accepts only Transport Layer Security (TLS) 1.2 connections." A cipher policy with no common cipher breaks sync the same way.</li>
<li><strong>BITS needs Range headers.</strong> Microsoft: "If you're behind a proxy server or behind a firewall that removes this header, the file copy operation is unsuccessful." Fix the proxy or inspection rule first. Foreground BITS is the documented fallback.</li>
<li><strong>Windows 11 needs two MIME types.</strong> <code>.msu</code> and <code>.wim</code> at the IIS server level, from a current cumulative update or by hand. Microsoft: "Clients using Windows 11 version 22H2 might fail to install updates from WSUS if the server isn't updated."</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Sync status, needing-files count, which updates, since when, what changed</td>
<td>"WSUS stopped downloading" with no dates</td>
</tr>
<tr>
<td>2. Server's own view</td>
<td>Last sync <code>Succeeded</code>, needing-files count falling, progress moving</td>
<td>Sync <code>Failed</code>; count stuck; bytes not moving</td>
</tr>
<tr>
<td>3. Update file settings</td>
<td>Storage, deferred download, express, and languages match what you expect</td>
<td>Files never requested; clients sent to Microsoft Update</td>
</tr>
<tr>
<td>4. Logs</td>
<td><code>SoftwareDistribution.log</code> and event 364 read for the exact reason</td>
<td>Errors nobody has read</td>
</tr>
<tr>
<td>5. Network path</td>
<td>Endpoint, ports 80/443 (or 8530/8531 upstream), proxy, inspection</td>
<td>Timeouts, 407, resets, downloads that stop at the same byte</td>
</tr>
<tr>
<td>6. TLS</td>
<td>TLS 1.2 and a cipher Microsoft Update accepts</td>
<td>"The underlying connection was closed"</td>
</tr>
<tr>
<td>7. Content folder</td>
<td>Free space, the right <code>ContentDir</code>, NETWORK SERVICE Full Control</td>
<td>Disk full; access denied; content directory not accessible</td>
</tr>
<tr>
<td>8. BITS</td>
<td>Jobs progressing; Range requests survive the path</td>
<td>Jobs stuck in transient error; Range header message</td>
</tr>
<tr>
<td>9. UUP and client download</td>
<td><code>.msu</code>/<code>.wim</code> MIME types; clients reach <code>Content</code> on 8530</td>
<td>Windows 11 updates never finish; clients at 0%</td>
</tr>
<tr>
<td>10. Disconnected server</td>
<td>Content copied, metadata imported, settings matched</td>
<td>Imported updates stuck in downloading</td>
</tr>
<tr>
<td>11. Change</td>
<td>Only after Steps 1-10 agree: one layer, one change</td>
<td>WsusContent deleted, same failure next month</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Last sync result and time</strong></td>
<td>Failed sync points at Steps 4-6; succeeded sync with stuck files points at Steps 3-9</td>
</tr>
<tr>
<td><strong>Updates needing files, now and yesterday</strong></td>
<td>A falling count is a slow download; a flat count is a stuck one</td>
</tr>
<tr>
<td><strong>Which updates</strong></td>
<td>All of them: the path or the disk. Only Windows 11 cumulative updates: UUP, size, or inspection</td>
</tr>
<tr>
<td><strong>Upstream source, proxy, firewall</strong></td>
<td>Microsoft Update, an upstream WSUS server, or export/import; proxies and inspection break Range requests and TLS</td>
</tr>
<tr>
<td><strong>What changed?</strong></td>
<td>Firewall or inspection profile, proxy, cipher GPO, TLS hardening, a new product, a moved content folder</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>Last sync <strong>Failed</strong>, error details blank</td>
<td>The known issue hides details in the console. Read the log</td>
<td>Steps 2, 4</td>
</tr>
<tr>
<td><code>SoapException: Fault occurred</code> with <code>fe2.update.microsoft.com</code> in the log</td>
<td>Old, decommissioned endpoint (Microsoft)</td>
<td>Step 5</td>
</tr>
<tr>
<td>"The underlying connection was closed"</td>
<td>TLS version or cipher mismatch, or a proxy that ends the connection</td>
<td>Step 6</td>
</tr>
<tr>
<td>Sync stays at 0%</td>
<td>Microsoft: the WSUS service "is in a stopped state"</td>
<td>Step 2</td>
</tr>
<tr>
<td>Sync succeeds, updates needing files never moves</td>
<td>The content download is the broken half</td>
<td>Steps 3, 4, 7, 8</td>
</tr>
<tr>
<td>Event 364 with a Range header message</td>
<td>A proxy or firewall strips HTTP 1.1 Range requests (Microsoft KB 922330)</td>
<td>Steps 5, 8</td>
</tr>
<tr>
<td>Event 364 with HTTP 503 or 401 from an upstream WSUS server</td>
<td>The upstream server is overloaded or rejected the request (Q&amp;A reports)</td>
<td>Step 5</td>
</tr>
<tr>
<td>Only Windows 11 cumulative updates stop part-way</td>
<td>UUP MIME types, disk, or content inspection on the firewall</td>
<td>Steps 7, 9</td>
</tr>
<tr>
<td>Server shows files present, clients sit at 0%</td>
<td>The client's download from <code>Content</code>, not the server's</td>
<td>Step 9</td>
</tr>
<tr>
<td>Disconnected server, imported updates stuck in downloading</td>
<td>Content not copied, or settings that don't match the export server</td>
<td>Step 10</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the server's own view (read-only)</h2>
<p>Start on the WSUS server, in an elevated PowerShell session with the UpdateServices module. One object gives you the sync result, the needing-files count, and the download progress:</p>
<pre><code class="language-powershell"># Read-only. Local WSUS server (no parameters = this server)
$wsus = Get-WsusServer

# Read-only. The last synchronization: result, times, error, and per-update import errors
$sub  = $wsus.GetSubscription()
$sub.GetSynchronizationStatus()
$last = $sub.GetLastSynchronizationInfo()
$last | Select-Object Result, StartTime, EndTime, Error, ErrorText
$last.UpdateErrors | Select-Object UpdateId, ErrorText -First 20

# Read-only. How many updates still lack files, and the bytes behind them
$wsus.GetStatus().UpdatesNeedingFilesCount
$wsus.GetContentDownloadProgress()
</code></pre>
<p>How Microsoft defines what you just read:</p>
<table>
<thead>
<tr>
<th>Read</th>
<th>Microsoft's definition</th>
<th>How to read it (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td><code>GetLastSynchronizationInfo().Result</code></td>
<td>"If the synchronization process has never run, ISynchronizationInfo.Result is SynchronizationResult.NeverRun." Microsoft's sample compares it with <code>Succeeded</code> and <code>Failed</code></td>
<td><code>Failed</code> sends you to Steps 4-6</td>
</tr>
<tr>
<td><code>Error</code></td>
<td>"If there was no error, then the value is NotApplicable."</td>
<td>A value here is the failure category; <code>ErrorText</code> carries the exception</td>
</tr>
<tr>
<td><code>UpdateErrors</code></td>
<td>Microsoft's sample checks it because the sync "could also fail with an import error"</td>
<td>Errors for single updates, not the whole sync</td>
</tr>
<tr>
<td><code>UpdatesNeedingFilesCount</code></td>
<td>"Number of updates on the WSUS server that do not have all the required installation files."</td>
<td>The console's needing-files number</td>
</tr>
<tr>
<td><code>GetContentDownloadProgress()</code></td>
<td>"The total number of bytes to download and the number of bytes downloaded so far."</td>
<td>Read it twice, ten minutes apart. No change means stuck</td>
</tr>
</tbody></table>
<p>Two traps in those reads. First, use <code>GetStatus()</code>, not <code>GetUpdateStatus()</code>, for the needing-files count. Microsoft on <code>GetUpdateStatus</code>: "This method updates only the UpdateCount, DeclinedUpdateCount, UpdatesWithClientErrorsCount, UpdatesNeededByComputersCount, and UpdatesUpToDateCount properties. All other properties will be 0 or empty." A Microsoft Q&amp;A poster saw it "always 0 even when the GUI says otherwise" until switching to <code>GetStatus()</code>. Second, when files live on Microsoft Update, the progress number isn't about update files at all. Microsoft: "If HostBinariesOnMicrosoftUpdate is true, the return value represents the download progress of the end-user license agreements (EULAs)."</p>
<p><strong>Why the console's error is blank.</strong> Microsoft lists it as a known issue on its Windows Server 2025 pages for the October 2025 WSUS security fix: "After installing KB5070881 or later updates, Windows Server Update Services (WSUS) does not display synchronization error details within its error reporting. This functionality is temporarily removed to address the Remote Code Execution Vulnerability, CVE-2025-59287." The same known issue still appears, with KB5070893, on Microsoft's August 11, 2026 Windows Server 2025 hotpatch page. We only checked Windows Server 2025 pages, and we haven't tested whether the API's <code>ErrorText</code> is affected. Don't roll back the security fix to get the error text back; read the log in Step 4.</p>
<p><strong>A sync that never leaves 0%.</strong> Microsoft's sync troubleshooting: if a manual synchronization starts but stays at 0%, the WSUS service "is in a stopped state." Read the services:</p>
<pre><code class="language-powershell"># Read-only. WSUS service, IIS, and the WSUS app pool (service names are our note)
Get-Service -Name WsusService, W3SVC | Format-Table Name, Status, StartType
Import-Module WebAdministration
Get-WebAppPoolState -Name 'WsusPool'
</code></pre>
<p>If <code>WsusPool</code> is stopped, the <a href="https://dev.to/adminpackstudio/wsus-clients-not-reporting-a-read-first-console-client-policy-proxy-and-susclientid-triage-54p7">clients-not-reporting post</a> has Microsoft's 503 and private memory limit guidance.</p>
<p>Microsoft's health check: "run the following command on the WSUS server. Then review the Application log in Event Viewer for errors":</p>
<pre><code class="language-console">:: Read-only health check. Results go to the Application log
"%ProgramFiles%\Update Services\Tools\wsusutil.exe" checkhealth
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>Result</code> is <code>Failed</code>, <code>ErrorText</code> empty or generic</td>
<td>The detail is in the log</td>
<td>Step 4</td>
</tr>
<tr>
<td><code>Result</code> is <code>Succeeded</code>, needing-files count flat for hours</td>
<td>Content download is stuck</td>
<td>Steps 3, 4, 7, 8</td>
</tr>
<tr>
<td>Count falls slowly, bytes move</td>
<td>It's working, just slow</td>
<td>Watch; BITS priority (Step 8) only if it stalls</td>
</tr>
<tr>
<td><code>UpdateErrors</code> lists a handful of updates</td>
<td>Import errors for those updates, not a broken sync</td>
<td>Step 4: search the log for those update IDs</td>
</tr>
</tbody></table>
<hr />
<h2>Step 3: The update file settings (read-only)</h2>
<p>Before you chase a download that isn't happening, check that the server is supposed to download anything. Microsoft: "Update files can be stored on your WSUS server or on Microsoft Update servers, depending on how you've configured your synchronization options." And: "If not, client computers will download approved updates directly from Microsoft Update."</p>
<pre><code class="language-powershell"># Read-only. Where updates come from, where files live, and what gets downloaded
$cfg = (Get-WsusServer).GetConfiguration()
$cfg | Select-Object SyncFromMicrosoftUpdate, UpstreamWsusServerName, UpstreamWsusServerPortNumber, UpstreamWsusServerUseSsl,
  IsReplicaServer, HostBinariesOnMicrosoftUpdate, DownloadUpdateBinariesAsNeeded, DownloadExpressPackages,
  AllUpdateLanguagesEnabled, LocalContentCachePath, UseProxy, ProxyName, ProxyServerPort, MUUrl
</code></pre>
<p>In the console: <strong>Options</strong> &gt; <strong>Update Files and Languages</strong>, and <strong>Options</strong> &gt; <strong>Update Source and Proxy Server</strong>. Microsoft's definitions:</p>
<table>
<thead>
<tr>
<th>Property</th>
<th>Microsoft's definition</th>
<th>What it changes here (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td><code>HostBinariesOnMicrosoftUpdate</code></td>
<td>"Gets or sets whether updates are stored locally or whether clients download approved updates directly from Microsoft Update."</td>
<td><code>True</code>: nothing to download on the server; clients need internet access</td>
</tr>
<tr>
<td><code>DownloadUpdateBinariesAsNeeded</code></td>
<td>"Gets or sets whether updates are downloaded only when they are approved."</td>
<td><code>True</code>: unapproved updates never get files. That's normal</td>
</tr>
<tr>
<td><code>DownloadExpressPackages</code></td>
<td>"Gets or sets whether express installation packages should be downloaded."</td>
<td><code>True</code>: much more content per update</td>
</tr>
<tr>
<td><code>SyncFromMicrosoftUpdate</code> / <code>UpstreamWsusServerName</code></td>
<td>Sync from Microsoft Update, or "the name of a local server from which to synchronize updates."</td>
<td>Which network path Step 5 has to test</td>
</tr>
<tr>
<td><code>UseProxy</code> / <code>ProxyName</code> / <code>ProxyServerPort</code></td>
<td>"Gets or sets whether to use a proxy to download updates."</td>
<td>The proxy Step 5 tests</td>
</tr>
</tbody></table>
<p>Microsoft's rule for the two download properties: "To download only approved updates, set HostBinariesOnMicrosoftUpdate to false and IUpdateServerConfiguration.DownloadUpdateBinariesAsNeeded to true." And: "WSUS ignores the following property values if HostBinariesOnMicrosoftUpdate is true," which are the express and as-needed settings.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>HostBinariesOnMicrosoftUpdate</code> is <code>True</code></td>
<td>The server never stores update files. Clients that can't reach Microsoft Update sit at 0% (our reading)</td>
<td>Step 9, or change the storage option deliberately (Step 11)</td>
</tr>
<tr>
<td>Update unapproved, <code>DownloadUpdateBinariesAsNeeded</code> <code>True</code></td>
<td>Microsoft: "When you defer downloads, an update is downloaded only after it's approved."</td>
<td>Not a fault</td>
</tr>
<tr>
<td>Downstream server in a hierarchy</td>
<td>Microsoft: a downstream request for an update not approved upstream "forces a download on the upstream server," and the downstream server gets it "on a subsequent synchronization"</td>
<td>Give it one more sync before you troubleshoot</td>
</tr>
<tr>
<td><code>DownloadExpressPackages</code> <code>True</code></td>
<td>Microsoft: "Express installation files are larger than the updates that are distributed to client computers because the express installation file contains all possible versions of each file that is to be updated."</td>
<td>Step 7: disk</td>
</tr>
<tr>
<td><code>MUUrl</code> not <code>https://sws.update.microsoft.com</code> on a server that syncs from Microsoft Update</td>
<td>Old endpoint</td>
<td>Step 5</td>
</tr>
</tbody></table>
<p>Microsoft: "By default, WSUS doesn't use express installation files." Turning them on to speed up a stuck download makes the download bigger (our reading).</p>
<hr />
<h2>Step 4: Read the logs: <code>SoftwareDistribution.log</code> and event 364 (read-only)</h2>
<p>Microsoft points at one log for sync and import errors, on the WSUS server: <code>%ProgramFiles%\Update Services\LogFiles\SoftwareDistribution.log</code>. Its sync troubleshooting also shows the start lines to look for: "A manual synchronization was started." (<code>EventId=382</code>) and "A scheduled synchronization was started." (<code>EventId=381</code>). Content download failures show up there and in the Application log as event 364.</p>
<pre><code class="language-powershell"># Read-only. The last sync start, then errors after it (patterns from Microsoft's samples; the selection is ours)
$log = Join-Path $env:ProgramFiles 'Update Services\LogFiles\SoftwareDistribution.log'
Select-String -Path $log -Pattern 'EventId=38[12]' | Select-Object -Last 3
Select-String -Path $log -Pattern 'ProcessWebServiceProxyException|SoapException|WebException|EventId=364|SCHANNEL Protocol' |
  Select-Object -Last 40 | ForEach-Object Line

# Read-only. Event 364 in the Application log, last 7 days (provider name from Microsoft's KB 922330 sample)
Get-WinEvent -FilterHashtable @{
  LogName      = 'Application'
  ProviderName = 'Windows Server Update Services'
  Id           = 364
  StartTime    = (Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Message | Format-List
</code></pre>
<p>For EULA problems during sync, Microsoft's tip for the same log: "Look for .txt in the log to find relevant entries."</p>
<p>Map the text you find to a layer. Each row's text is quoted from the Microsoft page or Q&amp;A thread named in it:</p>
<table>
<thead>
<tr>
<th>Text in the log or event</th>
<th>Source</th>
<th>Points at</th>
</tr>
</thead>
<tbody><tr>
<td><code>SoapException: Fault occurred</code> with an <code>fe2.update.microsoft.com</code> actor</td>
<td>Microsoft KB 4482416: that endpoint "was fully decommissioned and is no longer reachable after July 8, 2019."</td>
<td>Step 5: endpoint</td>
</tr>
<tr>
<td>"The underlying connection was closed: An unexpected error occurred on a send."</td>
<td>Microsoft's sync and import article: TLS 1.0 hello, reset from the endpoint</td>
<td>Step 6: TLS</td>
</tr>
<tr>
<td>"The client and server cannot communicate, because they do not possess a common algorithm"</td>
<td>Same article: no protocol or cipher in common</td>
<td>Step 6: TLS and ciphers</td>
</tr>
<tr>
<td>"A connection attempt failed because the connected party didn't properly respond after a period of time, or established connection failed because connected host didn't respond."</td>
<td>Microsoft's Configuration Manager sync article</td>
<td>Step 5: firewall, proxy</td>
</tr>
<tr>
<td><code>HTTP Status 407 Proxy Authentication Required</code> or <code>HTTP Status 502 Proxy Error</code></td>
<td>Same article's list of proxy and authentication errors</td>
<td>Step 5: proxy credentials</td>
</tr>
<tr>
<td>"Content file download failed. Reason: The server does not support the necessary HTTP protocol. Background Intelligent Transfer Service (BITS) requires that the server support the Range protocol header."</td>
<td>Microsoft KB 922330</td>
<td>Steps 5, 8: Range requests</td>
</tr>
<tr>
<td>"Content file download failed. Reason: HTTP status 503: The service is temporarily overloaded."</td>
<td>A Microsoft Q&amp;A post from a downstream server</td>
<td>The upstream WSUS server (Step 5)</td>
</tr>
<tr>
<td>Event 364 with <code>HTTP status 404</code></td>
<td>Microsoft Q&amp;A threads; the answers there point at missing or corrupt content and <code>wsusutil reset</code></td>
<td>Steps 7, 11</td>
</tr>
</tbody></table>
<p>KB 922330's console symptom for the Range case: "A red X appears over the update." The approval-time message Q&amp;A posters quote, "The files for this update have not yet been downloaded," only says the files aren't there yet. Event 364 says why (our reading).</p>
<hr />
<h2>Step 5: The path to Microsoft Update or the upstream server (read-only)</h2>
<p><strong>The endpoint.</strong> Microsoft's first check for sync or import problems is the endpoint in <code>MUUrl</code> (Step 3). <code>https://sws.update.microsoft.com</code> is the current one; <code>https://fe2.update.microsoft.com</code> "is decommissioned as a WSUS synchronization endpoint, and connections to it will fail." Microsoft: "Windows Server 2012 and later versions should be configured to use the <a href="https://sws.update.microsoft.com">https://sws.update.microsoft.com</a> endpoint." The fix is in Step 11.</p>
<p><strong>The firewall.</strong> Microsoft: "To get updates from Microsoft Update, the WSUS server uses ports 80 and 443 for the HTTP and HTTPS protocols." The topmost server needs outbound access to a list of Microsoft domains, including <code>*.update.microsoft.com</code>, <code>*.windowsupdate.com</code>, <code>download.windowsupdate.com</code>, <code>download.microsoft.com</code>, and <code>*.delivery.mp.microsoft.com</code>. Read the full list on Microsoft's Configure WSUS page. Microsoft is explicit about IP-based rules: "The IP addresses associated with these domains are constantly changing, so don't try to use IP address ranges instead." A downstream server only needs its upstream server: "By default, these ports are 8530 and 8531."</p>
<pre><code class="language-powershell"># Read-only. TCP reachability from the WSUS server (hosts from Microsoft's list; the selection is ours)
'sws.update.microsoft.com', 'download.windowsupdate.com' | ForEach-Object {
  Test-NetConnection -ComputerName $_ -Port 443 -WarningAction SilentlyContinue |
    Select-Object ComputerName, RemotePort, TcpTestSucceeded
}
Test-NetConnection -ComputerName download.windowsupdate.com -Port 80 -WarningAction SilentlyContinue |
  Select-Object ComputerName, RemotePort, TcpTestSucceeded

# Read-only. A downstream server: can it reach its upstream on both ports? (WSUS-HQ is an example)
foreach ($port in 8530, 8531) {
  Test-NetConnection -ComputerName WSUS-HQ.corp.contoso.com -Port $port -WarningAction SilentlyContinue |
    Select-Object ComputerName, RemotePort, TcpTestSucceeded
}
</code></pre>
<p><code>Test-NetConnection</code> "Specifies the TCP port number on the remote computer," so it proves TCP, not TLS or a full download. Behind a mandatory proxy, a failed direct test is expected (our note).</p>
<p><strong>The proxy.</strong> WSUS has its own proxy setting, separate from the browser's. Microsoft: "This setting will apply only when your WSUS server runs synchronizations. By default your WSUS server will try to connect directly to the upstream server or Microsoft Update." And: "Configure WSUS with the same port number that the proxy server is configured to use." Microsoft's sync walkthrough also describes the connection layer: "After a synchronization starts, the WSUS server attempts to make an HTTP connection through WinHTTP." So read the machine's WinHTTP proxy too:</p>
<pre><code class="language-console">:: Read-only. The system (WinHTTP) proxy on the WSUS server
netsh winhttp show proxy
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Direct TCP 443 fails, no proxy configured in WSUS</td>
<td>Firewall blocks the server, or a proxy is required and not set</td>
<td>Firewall team with Microsoft's domain list, or set the proxy (Step 11)</td>
</tr>
<tr>
<td><code>UseProxy</code> <code>True</code>, log shows 407</td>
<td>Proxy credentials or authentication type</td>
<td>Step 11: the console's proxy credentials</td>
</tr>
<tr>
<td>Downstream server: 8530/8531 fail to the upstream</td>
<td>Path or upstream IIS. A 503 from upstream: Microsoft's <code>WsusPool</code> guidance</td>
<td>The upstream server first</td>
</tr>
<tr>
<td>Downloads stop at the same point every time, sync works</td>
<td>Content inspection or AV scanning on the firewall (below)</td>
<td>Exclude the WSUS server from inspection (Step 11)</td>
</tr>
</tbody></table>
<p><strong>Firewall content inspection.</strong> In an April 2026 Microsoft Q&amp;A thread, a fresh WSUS on Windows Server 2022 stopped the 2026-02 and 2026-03 Windows 11 cumulative updates part-way, "with updates needing files: 1," while other updates downloaded fine. The poster's resolution: "Once I turned off content inspection for this server the update downloaded fully without issue." Microsoft's KB 922330 names one vendor case of the same kind: a SonicWALL device where the "Enable HTTP Byte-Range request with Gateway AV" setting isn't enabled. Beyond that case, treat inspection as a pattern to test (community report).</p>
<hr />
<h2>Step 6: TLS 1.2 and cipher suites (read-only)</h2>
<p>Microsoft: <code>https://sws.update.microsoft.com</code> "accepts only Transport Layer Security (TLS) 1.2 connections. And some ciphers were disabled." Its Configuration Manager TLS guidance: "TLS 1.2 is supported by default for WSUS on all currently supported version of Windows Server." So on a current server, a TLS failure usually means something was changed: a cipher policy, an SCHANNEL hardening script, or .NET settings (our reading).</p>
<p><strong>What WSUS logged at startup.</strong> Microsoft: "WSUS logs which SSL/TLS versions are enabled when it starts." Its sample lines after a WSUS service restart and <code>iisreset</code>:</p>
<pre><code class="language-text">SCHANNEL Protocol 'TLS 1.0' disabled
SCHANNEL Protocol 'TLS 1.1' disabled
SCHANNEL Protocols subkey for 'TLS 1.2' not found. Protocol is enabled
</code></pre>
<p>For Windows Server 2012 and 2012 R2 WSUS servers, Microsoft adds: "If you can't find these entries, it means the update that enables TLS 1.2 isn't installed." The Step 4 <code>Select-String</code> already searches for <code>SCHANNEL Protocol</code>.</p>
<p><strong>.NET strong cryptography.</strong> Microsoft's fix for import and sync failures over TLS 1.2 starts with the .NET registry values. Read them before you set anything:</p>
<pre><code class="language-powershell"># Read-only. .NET TLS values Microsoft's TLS 1.2 guidance sets (missing = not set)
'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\.NETFramework\v4.0.30319',
'HKLM:\SOFTWARE\Microsoft\.NETFramework\v2.0.50727' | ForEach-Object {
  Get-ItemProperty -Path $_ -ErrorAction SilentlyContinue | Select-Object @{ n = 'Key'; e = { $_.PSPath -replace '^.*::' } }, SchUseStrongCrypto, SystemDefaultTlsVersions
}
</code></pre>
<p>Microsoft: "The SchUseStrongCrypto setting allows .NET to use TLS 1.1 and TLS 1.2. The SystemDefaultTlsVersions setting allows .NET to use the OS configuration."</p>
<p><strong>Cipher suites set by policy.</strong> Microsoft's Issue 5 is a GPO-configured cipher order with nothing the endpoint accepts: "If the GPO is applied, and it doesn't specify one of these ciphers, communication with WU/MU fails." Its read:</p>
<pre><code class="language-console">:: Read-only. Then search the report for "SSL Cipher Suite Order"
gpresult /scope computer /h GPReport.html
</code></pre>
<p>Microsoft's list "As of August 2020" for Windows Server 2016 and later starts with <code>TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384</code> and <code>TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256</code>; read the full list on its page, because "This list will change over time because ciphers will gradually grow weaker as technology improves." Without a GPO, the local value is <code>Functions</code> under <code>HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography\Configuration\SSL\00010002</code>.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>"An unexpected error occurred on a send" and TLS 1.0/1.1 still in use by WSUS</td>
<td>The endpoint resets anything below TLS 1.2</td>
<td>Step 11: .NET values, restart</td>
</tr>
<tr>
<td>"do not possess a common algorithm" after someone disabled TLS 1.0/1.1</td>
<td>WSUS import "can't use TLS 1.2" without the fix</td>
<td>Step 11: .NET values or <code>w3wp.exe.config</code></td>
</tr>
<tr>
<td>GPO sets <strong>SSL Cipher Suite Order</strong> without the endpoint's ciphers</td>
<td>No common cipher</td>
<td>The GPO owner (Step 11)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: The content folder: space, location, and permissions (read-only)</h2>
<pre><code class="language-powershell"># Read-only. Where WSUS thinks its content is, and how much room is left
$setup = Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup'
$setup | Select-Object ContentDir, TargetDir
(Get-WsusServer).GetConfiguration().LocalContentCachePath
$drive = (Split-Path -Path $setup.ContentDir -Qualifier).TrimEnd(':')
Get-PSDrive -Name $drive | Select-Object Name, @{ n = 'FreeGB'; e = { [math]::Round($_.Free / 1GB, 1) } }, @{ n = 'UsedGB'; e = { [math]::Round($_.Used / 1GB, 1) } }

# Read-only. Who has rights on WsusContent (NETWORK SERVICE should have Full Control)
(Get-Acl -Path (Join-Path $setup.ContentDir 'WsusContent')).Access |
  Select-Object IdentityReference, FileSystemRights, AccessControlType, IsInherited

# Read-only. Does IIS serve Content from the same folder? (site name varies: 'WSUS Administration' or 'Default Web Site')
Import-Module WebAdministration
Get-WebVirtualDirectory -Site 'WSUS Administration' -Name 'Content' | Select-Object Path, PhysicalPath
</code></pre>
<p>Microsoft's reference for the registry: under <code>HKLM\Software\Microsoft\Update Services\Server\Setup</code>, <code>ContentDir</code> is "the location under which update binaries and end user license agreement files are stored," and <code>ContentDir\WsusContent</code> "contains the update files." The <code>Content</code> virtual directory lookup at the end is our way to compare the IIS path with <code>ContentDir</code>. Microsoft's reference lists <code>Content</code> as one of the virtual roots WSUS creates, with anonymous access.</p>
<p><strong>Space.</strong> Microsoft: "At a minimum, WSUS requires 20 GB to store updates locally; however, we recommend at least 40 GB." For Windows 11 UUP: "the WSUS server downloads approximately 10 GB of content per Windows version and processor architecture for each version. For example, an additional 20 GB of content is downloaded for both x64 and arm64 for Windows 11, version 22H2." Express files, all languages, and feature updates (the Upgrades classification) all add to that.</p>
<p><strong>Permissions.</strong> Microsoft's server settings reference: the WSUS content directory "must have Full Control permission granted to the NT Authority\Network Service account. This permission is set by WSUS server setup when it creates the directory, but it is possible that your security software might reset this." And: "Not having this permission set will also cause BITS downloads to fail." The same page says the root folder of the drive that holds WsusContent needs Read for the Users account or NETWORK SERVICE, a permission "that WSUS setup does not configure." That page dates from the WSUS 2.0/3.0 era, so treat its details as a checklist, not a current contract (our note).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Content drive nearly full</td>
<td>New files can't land. Microsoft warns that switching to local storage without room "may damage the WSUS installation."</td>
<td>Step 11: free space or <code>movecontent</code></td>
</tr>
<tr>
<td>NETWORK SERVICE missing or not Full Control on WsusContent</td>
<td>Microsoft: BITS downloads fail</td>
<td>Step 11: restore the permission</td>
</tr>
<tr>
<td><code>ContentDir</code> and the IIS <code>Content</code> path differ</td>
<td>A folder moved by hand. Clients ask IIS for files WSUS stored somewhere else</td>
<td>Step 11: <code>movecontent -skipcopy</code> to realign</td>
</tr>
<tr>
<td>A post-install or health message: "The WSUS content directory is not accessible." with a 503</td>
<td>A Microsoft Q&amp;A report; the 503 points at IIS and <code>WsusPool</code> (our reading)</td>
<td>The <code>WsusPool</code> checks in Step 2</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: BITS on the server (read-only)</h2>
<p>Microsoft: "WSUS uses the Background Intelligent Transfer Service (BITS) protocol for all its file transfer tasks. This includes downloads to client computers and server synchronizations." When the needing-files count is flat, look at the BITS jobs on the WSUS server:</p>
<pre><code class="language-powershell"># Read-only. All users' BITS jobs (needs admin). WSUS jobs aren't owned by your account
Get-BitsTransfer -AllUsers |
  Select-Object DisplayName, JobState, OwnerAccount, BytesTransferred, BytesTotal, ErrorDescription |
  Sort-Object JobState | Format-Table -AutoSize -Wrap

# Read-only. The BITS service itself
Get-Service -Name BITS | Select-Object Name, Status, StartType
</code></pre>
<p><code>-AllUsers</code> matters. Microsoft: "By default, the cmdlet returns the jobs that are owned by the current user." The selected properties are ours. Which job names and owner WSUS uses, we haven't confirmed on a Microsoft page, so read the list rather than filter it.</p>
<p><strong>Range requests and foreground mode.</strong> Microsoft's KB 922330 explains why some downloads fail behind a proxy or firewall: "When you copy a file by using BITS in background mode, the file is copied in multiple small parts. To perform this kind of copy operation, BITS uses the HTTP 1.1 Content-Range header." Then: "When BITS copies files in foreground mode, BITS doesn't use this header." Its first resolution is to fix the proxy: "Modify the proxy server settings to support HTTP 1.1 range requests. If you can't modify the proxy server in this manner, configure BITS to work in foreground mode." Microsoft's server settings reference describes the setting: "This internal setting specifies whether or not to use foreground priority for BITS downloads. The default is to use throttled downloads." Read it before you change it:</p>
<pre><code class="language-powershell"># Read-only. Current BITS priority for WSUS downloads (property form reported on Microsoft Q&amp;A; see the note)
(Get-WsusServer).GetConfiguration().BitsDownloadPriorityForeground
</code></pre>
<p>KB 922330's own commands update <code>tbConfigurationC</code> in SUSDB with SQL for WSUS 2.0 and 3.0. Microsoft Q&amp;A posters set the same value through the configuration object on current versions. Microsoft's property list for <code>IUpdateServerConfiguration</code> that we fetched doesn't show it, so treat the PowerShell form as community-documented (our note).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>WSUS jobs in a transient or error state with a Range message</td>
<td>The KB 922330 case</td>
<td>Fix the proxy or firewall first; foreground mode if you can't (Step 11)</td>
</tr>
<tr>
<td>Jobs transferring, bytes moving slowly</td>
<td>Throttled background mode on a busy link</td>
<td>Leave it, or foreground mode as a deliberate choice</td>
</tr>
<tr>
<td>No jobs at all, count flat</td>
<td>Nothing queued: settings (Step 3), disk (Step 7), or WSUS service</td>
<td>Steps 2, 3, 7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Windows 11 UUP and the client's download (read-only)</h2>
<p><strong>UUP MIME types on the server.</strong> Windows 11 quality updates come as Unified Update Platform content on WSUS. Microsoft: "Install one of the following updates on the WSUS servers, or manually add the required MIME types for UUP to the WSUS server," with the 2023-02 cumulative update (or later) listed for Windows Server 2016, 2019, and 2022. The two types are <code>.msu</code> (<code>application/octet-stream</code>) and <code>.wim</code> (<code>application/x-ms-wim</code>). Microsoft: "Ensure you selected the server and not the site when adding the MIME types. The WSUS Administration site needs the MIME type entry to be inherited rather than local."</p>
<pre><code class="language-powershell"># Read-only. Server-level static content MIME map for the two UUP types (the PSPath form is ours)
Import-Module WebAdministration
Get-WebConfigurationProperty -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/staticContent' -Name collection |
  Where-Object fileExtension -in '.msu', '.wim' | Select-Object fileExtension, mimeType
</code></pre>
<p>If someone added them by hand at the wrong level, you'll see Microsoft's documented error: "Cannot add duplicate collection entry of type 'mimeMap' with unique key attribute 'fileExtension' set to '.wim'". Microsoft: "These two MIME types should only be set to local at the server level."</p>
<p><strong>How clients get the files.</strong> With local storage, clients download from the WSUS server's <code>Content</code> virtual directory over plain HTTP. Microsoft: one port "uses TLS/HTTPS to send update metadata," and "A second port uses HTTP to send update payloads." By default that's 8530; <code>Content</code> is on Microsoft's list of virtual roots that shouldn't require TLS. Payloads are still protected: "When an update is downloaded, the client software verifies the payload's digital signature and hash."</p>
<p>When the server has every file and clients still sit at 0%, the client side has its own documented causes:</p>
<table>
<thead>
<tr>
<th>Client finding</th>
<th>Microsoft's explanation</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Proxy in Internet Explorer settings only</td>
<td>"If a proxy was configured in Internet Explorer (User level) but not in WinHTTP (System level), connections to Windows Update will fail."</td>
<td><code>netsh winhttp show proxy</code> on the client</td>
</tr>
<tr>
<td>Proxy strips Range requests</td>
<td>"Windows Update uses WinHttp with Partial Range requests (RFC 7233) to download updates and applications from Windows Update servers or on-premises WSUS servers."</td>
<td>Allow Range requests on the proxy</td>
</tr>
<tr>
<td><code>0x800706D9</code>, stuck at 0%</td>
<td>"You observe that the download for updates is stuck at 0%, especially in Windows Server 2016, where the Background Intelligent Transfer Service (BITS) service is the default Download Manager." Cause: the Windows Defender Firewall service is disabled</td>
<td>Enable the service. Microsoft: "Stopping the service associated with Windows Firewall with Advanced Security isn't supported by Microsoft."</td>
</tr>
<tr>
<td>Client never scans or reports</td>
<td>Not a content problem</td>
<td>The <a href="https://dev.to/adminpackstudio/wsus-clients-not-reporting-a-read-first-console-client-policy-proxy-and-susclientid-triage-54p7">clients-not-reporting post</a></td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Disconnected (offline) WSUS servers (read-only)</h2>
<p>Microsoft's Configure WSUS page describes the air-gapped design: one internet-connected WSUS server collects updates, they're "exported from the first server onto removable media, carried across the air gap, and imported into the second server." Export and import move metadata only. Microsoft's WSUSutil reference: "You cannot use this parameter to export update files, update approvals, or server settings." The files travel separately: Microsoft's Configuration Manager procedure copies "all files and folders" from the export server's WSUSContent folder to the disconnected server's WSUSContent folder, then runs <code>wsusutil.exe export packagename logfile</code> on one side and <code>wsusutil.exe import packagename logfile</code> on the other.</p>
<p>Microsoft's older disconnected-network guide adds the step that's easy to skip: "Make sure that the options for express installation files and update languages on the exporting server are compatible with the settings on the importing server."</p>
<table>
<thead>
<tr>
<th>Finding on the disconnected server</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Imported and approved updates stuck in downloading</td>
<td>The files for those updates aren't in WsusContent, or not at the paths WSUS expects. A Microsoft Q&amp;A thread describes exactly this after import</td>
<td>Re-copy the whole WsusContent folder from the export server</td>
</tr>
<tr>
<td>Export server itself shows updates needing files</td>
<td>It never had the files to give</td>
<td>Fix the export server first (Steps 2-8), then export again</td>
</tr>
<tr>
<td>Express or language settings differ between the two servers</td>
<td>The export doesn't carry what the importing server is set to expect</td>
<td>Match the settings (Step 11)</td>
</tr>
<tr>
<td>Import fails</td>
<td>Microsoft: "the user that runs the WSUSUtil tool must be a member of the local Administrators group on each server."</td>
<td>Rerun as a local admin with a fresh package</td>
</tr>
</tbody></table>
<p>In that Q&amp;A thread, the MVP answer suggests running <code>wsusutil reset</code> on the online server, waiting, then exporting again and copying the whole WsusContent folder (community answer).</p>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Deleting WsusContent</strong></td>
<td>Throws away every file that did download, then re-fetches all of it through the same path. Updates imported from the Microsoft Update Catalog have to be imported again (our reading, from Microsoft's cleanup note below)</td>
<td>Read the 364 reason first (Step 4)</td>
</tr>
<tr>
<td><strong>Reinstalling the WSUS role</strong></td>
<td>A new server behind the same proxy, firewall, cipher policy, and disk (our reading)</td>
<td>Steps 4-7 name the layer</td>
</tr>
<tr>
<td><strong><code>wsusutil reset</code> as the first move</strong></td>
<td>It re-checks every update against the files, and the Q&amp;A answers describe waiting about 24 hours with "no notifications of when this process is running or finished" (community). It doesn't fix a blocked path</td>
<td>Use it after the path is fixed, or for missing or corrupt files (Step 11)</td>
</tr>
<tr>
<td><strong>Re-enabling TLS 1.0/1.1 or dropping ciphers back in</strong></td>
<td>The endpoint "accepts only Transport Layer Security (TLS) 1.2 connections," so it doesn't help sync, and it weakens the server (our reading)</td>
<td>Step 6: the .NET values and the cipher GPO</td>
</tr>
<tr>
<td><strong>Switching to "Do not store updates locally" to make the alert go away</strong></td>
<td>Clients then download "directly from Microsoft Update," so every client needs internet access to the download domains</td>
<td>Fix the server's download (Steps 5-8)</td>
</tr>
<tr>
<td><strong>Turning on express files to speed things up</strong></td>
<td>Microsoft: express files need "additional bandwidth between your WSUS server, any upstream WSUS servers, and Microsoft Update, and requires additional local disk space"</td>
<td>Leave the default unless LAN bandwidth is the problem</td>
</tr>
<tr>
<td><strong>Running the cleanup wizard during triage</strong></td>
<td>Microsoft: "all the private update files that you've downloaded from the Microsoft Update Catalog site are also removed."</td>
<td>Run cleanup as maintenance, after the download works</td>
</tr>
<tr>
<td><strong>Adding the UUP MIME types at the site level</strong></td>
<td>Microsoft: the WSUS Administration site "needs the MIME type entry to be inherited rather than local." Duplicate entries break later updates</td>
<td>Server level only, or install the cumulative update</td>
</tr>
</tbody></table>
<hr />
<h2>Step 11: Change one layer, then prove it (only after the read)</h2>
<p>Pick the one change Steps 1-10 point at. Save the current configuration first, so you can compare or put it back:</p>
<pre><code class="language-powershell"># Low-risk write. Save the WSUS configuration and setup key before any change (our habit)
New-Item -Path C:\Temp -ItemType Directory -Force | Out-Null
(Get-WsusServer).GetConfiguration() | Export-Clixml -Path C:\Temp\wsus-config-before.xml
reg export 'HKLM\SOFTWARE\Microsoft\Update Services\Server\Setup' C:\Temp\wsus-setup-before.reg /y
</code></pre>
<p><strong>The endpoint (Step 5).</strong> Microsoft's script from KB 4482416, for WSUS on Windows Server 2012 and later. Microsoft: "Don't run the scripts on a WSUS server that's not the topmost server." The read lines come first in Microsoft's own version:</p>
<pre><code class="language-powershell"># Change, on the topmost WSUS server only. Microsoft's KB 4482416 script; close all WSUS consoles first
$server = Get-WsusServer
$config = $server.GetConfiguration()
# Check current settings before you change them
$config.MUUrl
$config.RedirectorChangeNumber
# Update the settings if MUUrl is https://fe2.update.microsoft.com/v6
$config.MUUrl = "https://sws.update.microsoft.com"
$config.RedirectorChangeNumber = 4002
$config.Save()
iisreset
Restart-Service *Wsus* -v
</code></pre>
<p><strong>The upstream source (Step 3).</strong> <code>Set-WsusServerSynchronization</code> supports <code>-WhatIf</code>:</p>
<pre><code class="language-powershell"># Change. Dry-run first (WSUS-HQ is an example upstream; or use -SyncFromMU for Microsoft Update)
Set-WsusServerSynchronization -UssServerName 'WSUS-HQ.corp.contoso.com' -PortNumber 8531 -UseSsl -WhatIf
</code></pre>
<p><strong>The proxy and firewall (Step 5).</strong> Set the proxy under <strong>Options</strong> &gt; <strong>Update Source and Proxy Server</strong> &gt; <strong>Proxy Server</strong>, with credentials if it needs them. For Range requests or content inspection, ask for an exception scoped to the WSUS server before you touch BITS, and test with one large update (our view).</p>
<p><strong>TLS (Step 6).</strong> Microsoft's Resolution for issue 1: "Configure .NET Framework to use TLS 1.2 by using registry keys," then "Restart the server after you set the registry keys." The values, from Microsoft's TLS 1.2 guidance (repeat for <code>WOW6432Node</code> and <code>v2.0.50727</code> as listed there):</p>
<pre><code class="language-powershell"># Change. Microsoft's .NET strong-crypto values, 64-bit v4 key shown. Dry-run first, then restart the server
$key = 'HKLM:\SOFTWARE\Microsoft\.NETFramework\v4.0.30319'
Set-ItemProperty -Path $key -Name SchUseStrongCrypto -Value 1 -Type DWord -WhatIf
Set-ItemProperty -Path $key -Name SystemDefaultTlsVersions -Value 1 -Type DWord -WhatIf
</code></pre>
<p>For a cipher GPO, change the policy at its source to include one of Microsoft's listed ciphers; don't edit the local key under a GPO that will rewrite it (our reading).</p>
<p><strong>BITS foreground mode (Step 8).</strong> Only after the proxy owner says Range requests can't be allowed. Our form, through the configuration object (community-documented, see Step 8), then Microsoft's documented restart of the Update Services service:</p>
<pre><code class="language-powershell"># Change. Foreground BITS for WSUS downloads. Read first, set, save, then restart WSUS (dry-run the restart)
$cfg = (Get-WsusServer).GetConfiguration()
$cfg.BitsDownloadPriorityForeground
$cfg.BitsDownloadPriorityForeground = $true
$cfg.Save()
Restart-Service -Name WsusService -WhatIf
</code></pre>
<p><strong>Disk and location (Step 7).</strong> Free space first: decline what you don't need, then cleanup as maintenance. To move content, Microsoft's syntax is <code>wsusutil.exe movecontent contentpath logfile [-skipcopy]</code>; <code>-skipcopy</code> "enables you to change the location of local WSUS update storage without copying any files" (Microsoft's archived WSUS blog). Create the new folder first:</p>
<pre><code class="language-console">:: Change. Move content to E:\WSUS and log it (paths are examples). Run from %ProgramFiles%\Update Services\Tools
wsusutil.exe movecontent E:\WSUS E:\WSUS-move.log
</code></pre>
<p>Use <code>-skipcopy</code> only when the files are already at the new path (our reading).</p>
<p><strong>Permissions (Step 7).</strong> Save the current ACL, then restore NETWORK SERVICE Full Control on WsusContent. <code>(OI)(CI)</code> makes it inherit to files and subfolders; <code>F</code> is full access:</p>
<pre><code class="language-powershell"># Low-risk write: save the ACL first. Then the change (D:\WSUS is an example ContentDir)
icacls 'D:\WSUS\WsusContent' /save C:\Temp\wsuscontent-acl.txt /t
icacls 'D:\WSUS\WsusContent' /grant 'NT AUTHORITY\NETWORK SERVICE:(OI)(CI)F'
</code></pre>
<p><strong>UUP MIME types (Step 9).</strong> Install the current cumulative update on the WSUS server first. If you can't, add both types at the IIS server node; Microsoft's Windows IT Pro blog has the <code>appcmd</code> form:</p>
<pre><code class="language-powershell"># Change, server level. From Microsoft's Windows IT Pro blog; only if the read in Step 9 found them missing
&amp; $Env:WinDir\system32\inetsrv\appcmd.exe set config /section:staticContent /+"[fileExtension='.msu',mimeType='application/octet-stream']"
&amp; $Env:WinDir\system32\inetsrv\appcmd.exe set config /section:staticContent /+"[fileExtension='.wim',mimeType='application/x-ms-wim']"
</code></pre>
<p><strong>Missing or corrupt files (Steps 4, 7).</strong> Once the path, disk, and permissions are right, Microsoft's tool for the content itself:</p>
<pre><code class="language-console">:: Change. Re-verifies content against the database and re-downloads what's missing or corrupt
"%ProgramFiles%\Update Services\Tools\wsusutil.exe" reset
</code></pre>
<p>Microsoft: "If update files are missing or have been corrupted, WSUS downloads the update files again." The API equivalent is <code>ResetAndVerifyContentState()</code>, which verifies "that all update files on the WSUS server are valid." Run one or the other once, then wait. For a disconnected server, match settings, re-copy WsusContent, and import a fresh package (Step 10).</p>
<p><strong>Then prove it.</strong> Run one manual sync, then read the same values as Step 2:</p>
<pre><code class="language-powershell"># Read-only. Sync result, the needing-files count, bytes, and any new 364s since the change
$wsus = Get-WsusServer
$wsus.GetSubscription().GetLastSynchronizationInfo() | Select-Object Result, StartTime, EndTime, Error
$wsus.GetStatus().UpdatesNeedingFilesCount
$wsus.GetContentDownloadProgress()
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'Windows Server Update Services'; Id = 364; StartTime = (Get-Date).AddHours(-2) } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Message
</code></pre>
<p>Read the count again an hour later and the next morning; it should fall. Then check one pilot client: its download should move past 0%.</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Old <code>MUUrl</code> endpoint</td>
<td>Microsoft's KB 4482416 script on the topmost server</td>
<td>Running it on downstream servers</td>
</tr>
<tr>
<td>TLS or cipher mismatch</td>
<td>.NET strong-crypto values; fix the cipher GPO at its source</td>
<td>Re-enabling TLS 1.0/1.1</td>
</tr>
<tr>
<td>Range headers stripped</td>
<td>A proxy or firewall exception for the WSUS server</td>
<td>Foreground BITS as the first move</td>
</tr>
<tr>
<td>Content inspection breaks large files</td>
<td>An inspection exception scoped to the WSUS server</td>
<td>Turning inspection off for the whole network</td>
</tr>
<tr>
<td>Disk full</td>
<td>Decline unneeded updates, cleanup, or <code>movecontent</code> to a bigger drive</td>
<td>Deleting WsusContent</td>
</tr>
<tr>
<td>Windows 11 UUP files missing</td>
<td>Current cumulative update on the WSUS server, or MIME types at server level</td>
<td>MIME types on the WSUS Administration site</td>
</tr>
<tr>
<td>Files missing after a restore or a fixed path</td>
<td><code>wsusutil reset</code> once, then wait</td>
<td>Reset, delete, reinstall in the same afternoon</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why is WSUS not downloading updates?</strong>
Either synchronization is failing (no new metadata) or the content download is failing (metadata, no files). Read the last sync result and <code>GetStatus().UpdatesNeedingFilesCount</code> first. Then read <code>SoftwareDistribution.log</code> and event 364 for the exact reason: an old endpoint, TLS or ciphers, a proxy or firewall, a full or locked-down content folder, or BITS Range requests.</p>
<p><strong>What does "updates needing files" mean in WSUS?</strong>
Updates that are approved, or set to download, whose files aren't all on the server yet. Microsoft defines the count as updates "that do not have all the required installation files." With deferred downloads on, unapproved updates don't count against you, because they're only downloaded "after it's approved."</p>
<p><strong>How do I fix WSUS sync failed "The underlying connection was closed"?</strong>
It's usually TLS. Microsoft's sync endpoint accepts only TLS 1.2, and some ciphers were removed. Set the .NET <code>SchUseStrongCrypto</code> and <code>SystemDefaultTlsVersions</code> values, check for a cipher suite GPO, restart, and sync again. On a server that syncs through a proxy, check that the proxy isn't ending the connection.</p>
<p><strong>Does wsusutil reset delete updates?</strong>
No. Microsoft: it verifies "that every update metadata row in the WSUS database corresponds to update files stored in the local update file storage location," and re-downloads files that are missing or corrupt. It can take a long time and doesn't fix a blocked network path, so fix the path first.</p>
<p><strong>Why won't WSUS download Windows 11 updates?</strong>
Check the UUP requirements first: the <code>.msu</code> and <code>.wim</code> MIME types at the IIS server level (or a current cumulative update on the WSUS server) and enough disk for about 10 GB per Windows version and architecture. If downloads stop part-way at the same point, check firewall content inspection, which a 2026 Microsoft Q&amp;A thread identified for Windows 11 cumulative updates.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/wsus-clients-not-reporting-a-read-first-console-client-policy-proxy-and-susclientid-triage-54p7">WSUS Clients Not Reporting</a> (the client half: policy, scan source, client proxy, IIS and <code>WsusPool</code>, and duplicate <code>SusClientId</code> values)</li>
<li><a href="https://dev.to/adminpackstudio/windows-update-rings-for-intune-pilots-pilot-vs-broad-deferrals-and-why-minimum-os-compliance-dmp">Windows Update Rings for Intune Pilots</a> (if WSUS's deprecation has you planning cloud-managed updates for some devices)</li>
<li><a href="https://dev.to/adminpackstudio/intune-feature-updates-ring-deferral-vs-a-feature-updates-policy-vs-windows-autopatch-which-one-3ck4">Intune Feature Updates: Ring Deferral vs a Feature Updates Policy vs Windows Autopatch</a> (which cloud tool owns feature updates once WSUS doesn't)</li>
</ul>
<h2>Free checklist</h2>
<p>A WSUS server that looked healthy until a firewall inspection profile changed is a change nobody tested from where the files flow. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=wsus-not-downloading-updates">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=wsus-not-downloading-updates</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=wsus-not-downloading-updates">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=wsus-not-downloading-updates</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-10 favor read-only checks: <code>Get-WsusServer</code>, <code>GetSubscription()</code> / <code>GetLastSynchronizationInfo()</code> / <code>GetSynchronizationStatus()</code>, <code>GetStatus()</code>, <code>GetContentDownloadProgress()</code>, <code>GetConfiguration()</code>, <code>Get-Service</code>, <code>Get-WebAppPoolState</code>, <code>wsusutil checkhealth</code>, <code>Select-String</code> on <code>SoftwareDistribution.log</code>, <code>Get-WinEvent</code>, <code>Test-NetConnection</code>, <code>netsh winhttp show proxy</code>, <code>Get-ItemProperty</code>, <code>gpresult</code>, <code>Get-PSDrive</code>, <code>Get-Acl</code>, <code>Get-WebVirtualDirectory</code>, <code>Get-BitsTransfer -AllUsers</code>, and <code>Get-WebConfigurationProperty</code>. <code>Export-Clixml</code>, <code>reg export</code>, and <code>icacls /save</code> write local files. The endpoint script, <code>Set-WsusServerSynchronization</code>, proxy settings, firewall exceptions, the .NET TLS values, cipher policy changes, <code>BitsDownloadPriorityForeground</code>, <code>wsusutil movecontent</code>, <code>icacls /grant</code>, MIME type changes, <code>wsusutil reset</code>, <code>ResetAndVerifyContentState()</code>, export/import, and service restarts are deliberate changes. Change one layer at a time, test with one update first, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[DFS Namespace Not Accessible: A Read-First Referral, Root Target, Site, and Folder Target Triage]]></title><description><![CDATA[DFS namespace not accessible? Read the referral cache, root targets, DC and PDC reach, site, and folder target before you recreate it.]]></description><link>https://adminpackstudio.hashnode.dev/dfs-namespace-not-accessible-a-read-first-referral-root-target-site-and-folder-target-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/dfs-namespace-not-accessible-a-read-first-referral-root-target-site-and-folder-target-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 08:40:12 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "Since this morning, every user at the BR1 branch gets 'Windows cannot access <code>\\corp.contoso.com\Shares\Finance</code>' with 'Error code 0x80070035 The network path was not found.' on their F: drive. HQ users are fine, and <code>\\BR-FS01\Finance</code> opens by server name from the same branch PCs. Last night the network team re-addressed the branch to 10.40.30.0/24. A forum thread says to delete and recreate the namespace, run <code>dfsutil /clean</code> on the namespace servers, and put the domain controllers in every branch PC's hosts file. Can we do that tonight?"</p>
<p>Not yet. <strong>A DFS namespace path is a chain of referrals, and "not accessible" means one link in that chain didn't answer.</strong> Microsoft's access-failure article gives three places it breaks: "Failure to connect to a domain controller to obtain a DFSN namespace referral", "Failure to connect to a DFSN server", and "Failure of the DFSN server to provide a folder referral". Deleting and recreating the namespace touches none of them if the real break is a subnet that isn't in AD, a folder set to in-site referrals only, or a folder target that's offline. Work through it as a chain: <strong>the exact path, error, and who is affected</strong> → <strong>the client's referral cache: which referral came back, and which target is ACTIVE</strong> → <strong>connectivity to a DC and to each namespace server, by IP</strong> → <strong>name resolution for every server name in the referral</strong> → <strong>the namespace itself: root, root targets, folders, folder targets, and the DFS Namespace service</strong> → <strong>AD and the PDC emulator: are the namespace servers reading current metadata?</strong> → <strong>sites and referral ordering: which site does DFS think the client and each target are in?</strong> → <strong>the folder target's own share, permissions, and SMB settings</strong> → <strong>the management side, when DFS Management says "The namespace cannot be queried"</strong>. <code>dfsutil cache referral</code>, <code>dfsutil cache domain</code>, <code>net view</code> by IP, <code>Test-NetConnection</code>, <code>Resolve-DnsName</code>, <code>Get-DfsnRoot</code>, <code>Get-DfsnRootTarget</code>, <code>Get-DfsnFolderTarget</code>, <code>Get-DfsnServerConfiguration</code>, <code>dfsdiag</code>, <code>repadmin /showrepl</code>, and <code>Get-ADReplicationSubnet</code> are the sources for that chain. Recreate a namespace for a branch whose subnet isn't in AD, and the branch still gets no referral the next morning.</p>
<p>This post covers DFS <strong>Namespaces</strong> (the <code>\\domain\root\folder</code> paths and their referrals), not DFS <strong>Replication</strong>. If the namespace hands out the right target but the files on two targets don't match, that's the replication layer, and <a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication Not Working</a> covers it. If the namespace path lives in a drive map that never arrives, <a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> covers the GPO side. This post covers <strong>"a domain-based or stand-alone DFS namespace path (or one folder in it) is not accessible from some or all clients, or DFS Management reports 'The namespace cannot be queried', and you need a read-first path through the referral cache, DC and namespace server reach, name resolution, namespace configuration, AD metadata, sites, and the folder target before you delete, clean, or recreate anything."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>Read the client's referral cache before you flush it.</strong> Microsoft: "If you cannot find an entry for the desired namespace, this is evidence that the domain controller did not return a referral." An entry with no ACTIVE target means the client got the list but reached none of the servers on it.</li>
<li><strong>Test servers by IP, without the share.</strong> Microsoft's test is <code>net view</code> against the IP alone, because with the namespace share in the path "you may unknowingly be referred to another DFS root server."</li>
<li><strong>In-site referrals can hide a whole branch.</strong> Microsoft: "If no same-site targets exist, the client does not receive a referral and cannot access that portion of the namespace." A re-addressed subnet that isn't in AD Sites and Services puts a client in no site at all (our reading of the site-mapping text).</li>
<li><strong>Offline is a setting, not only an outage.</strong> Microsoft: "Clients do not receive referrals for a DFS namespace target that is offline."</li>
<li><strong>Changes start at the PDC emulator.</strong> Microsoft: "When an administrator makes a change to the domain-based namespace, the change is made on the Primary Domain Controller (PDC) emulator master." A PDC that namespace servers can't reach shows up as stale or missing referrals.</li>
<li><strong>Don't clean before you export.</strong> Microsoft's cleanup steps "should only be used if recovery of the configuration data is not possible or is not desired." Export the namespace with <code>dfsutil root export</code> first.</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Exact path, error code, affected users and sites, and what changed written down</td>
<td>"DFS is down" with nothing else</td>
</tr>
<tr>
<td>2. Referral cache</td>
<td>The client holds a root referral and a folder referral, each with an ACTIVE target</td>
<td>No entry for the namespace, or no ACTIVE target</td>
</tr>
<tr>
<td>3. Connectivity</td>
<td>The client reaches a DC and each namespace server by IP over SMB</td>
<td><code>net view \\IP</code> fails; TCP 445 blocked</td>
</tr>
<tr>
<td>4. Name resolution</td>
<td>Every server name in the referral resolves to the right address</td>
<td>Works by FQDN, fails by short name (or the reverse)</td>
</tr>
<tr>
<td>5. The namespace</td>
<td>Root, root targets, folder, and folder targets online; DFS Namespace service running</td>
<td>A target set offline; one root server with its service stopped</td>
</tr>
<tr>
<td>6. AD and the PDC</td>
<td>Namespace servers read current metadata from the PDC emulator or a nearby DC</td>
<td>A change made an hour ago still isn't in the referrals</td>
</tr>
<tr>
<td>7. Sites and ordering</td>
<td>The client and every target map to the right AD site</td>
<td>Branch users sent to HQ, or no referral at all</td>
</tr>
<tr>
<td>8. The folder target</td>
<td>The target share and its permissions and SMB settings accept the client</td>
<td>Namespace path fails exactly like <code>\\server\share</code> fails</td>
</tr>
<tr>
<td>9. Management side</td>
<td>DFS Management can query the namespace</td>
<td>"The namespace cannot be queried" plus a reason</td>
</tr>
<tr>
<td>10. Change</td>
<td>Only after Steps 1-9 agree: one layer, one change</td>
<td>Recreated the namespace, branch still fails</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>The exact path the user opened</strong></td>
<td><code>\\corp.contoso.com\Shares</code>, <code>\\CONTOSO\Shares</code>, and <code>\\FS01\Shares</code> take different referral paths. Note FQDN vs NetBIOS domain name</td>
</tr>
<tr>
<td><strong>Root or one folder?</strong></td>
<td>The root failing points at DCs and namespace servers. One folder failing points at that folder's targets and settings</td>
</tr>
<tr>
<td><strong>The exact error text and code</strong></td>
<td>0x80070035 (network path not found) and 0x80070002 (cannot find) are both on Microsoft's list, with different likely layers (table below)</td>
</tr>
<tr>
<td><strong>Who and where</strong></td>
<td>One user, one PC, one site, VPN only, or everyone</td>
</tr>
<tr>
<td><strong>Does <code>\\server\share</code> work directly?</strong></td>
<td>If the folder target opens by server name, the file server and its permissions are probably fine and the referral layer is suspect</td>
</tr>
<tr>
<td><strong>Client OS build</strong></td>
<td>Windows 11 24H2 changed SMB signing defaults, which matters for third-party targets (Step 8)</td>
</tr>
<tr>
<td><strong>What changed?</strong></td>
<td>New subnet or re-addressing, a target added or removed, a namespace server retired, a DC moved, a firewall rule, a NAS firmware update</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>"Windows cannot access" the namespace path, 0x80070035 "The network path was not found"</td>
<td>Microsoft's Vista-and-later symptom for the three referral failures</td>
<td>Steps 2, 3</td>
</tr>
<tr>
<td>Works at HQ, fails at one branch</td>
<td>Sites: no target in that site with in-site referrals on, or the client subnet isn't in AD</td>
<td>Steps 2, 7</td>
</tr>
<tr>
<td>Root opens, one folder fails</td>
<td>That folder's targets: offline, unreachable, or excluded by site</td>
<td>Steps 5, 7, 8</td>
</tr>
<tr>
<td>Works by <code>\\corp.contoso.com\...</code>, fails by <code>\\CONTOSO\...</code> (or the reverse)</td>
<td>Name resolution of the names in the referral</td>
<td>Step 4</td>
</tr>
<tr>
<td>"Windows cannot find" the path, 0x80070002 "The system cannot find the file specified."</td>
<td>Microsoft lists it under namespace configuration-data problems</td>
<td>Steps 5, 6</td>
</tr>
<tr>
<td>"is not accessible. You might not have permission to use this network resource." with "Configuration information could not be read from the domain controller"</td>
<td>Microsoft's older-client wording for the same referral failures</td>
<td>Steps 2, 3, 6</td>
</tr>
<tr>
<td>A target was changed and some clients now get "Element not found"</td>
<td>A stale cached referral (a Microsoft Q&amp;A community report)</td>
<td>Step 2</td>
</tr>
<tr>
<td>Fails only to a NAS or non-Windows folder target, only on newer Windows 11</td>
<td>SMB signing or guest access on the target</td>
<td>Step 8</td>
</tr>
<tr>
<td>DFS Management shows "The namespace cannot be queried"</td>
<td>The management path: service, ports, PDC, registry. Users may be fine</td>
<td>Step 9</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the client's referral cache (read-only)</h2>
<p>Reproduce the failure once on the affected PC, then read what the DFS client cached. <code>dfsutil</code> comes with the DFS Management Tools. Microsoft's install command for a client is <code>Add-WindowsCapability -Name Rsat.FileServices.Tools~~~~0.0.1.0 -Online</code>, which is a small install, not a read. If you'd rather not touch the PC, run the same reads from an admin workstation in the same subnet (our habit).</p>
<pre><code class="language-console">:: Read-only. Reproduce once, then read the referral cache and the domain cache
dir \\corp.contoso.com\Shares\Finance
dfsutil cache referral
dfsutil cache domain
</code></pre>
<p>Microsoft's KB 975440 uses the older <code>dfsutil /pktinfo</code> (referral cache, "also known as the PKT cache") and <code>dfsutil /spcinfo</code> (domain cache) forms. Current <code>dfsutil</code> has a <code>cache</code> parameter that "Displays or flushes the client cache." The mapping of <code>cache referral</code> / <code>cache domain</code> to the KB's switches is ours; <code>dfsutil cache /?</code> on your build shows the exact options. Microsoft's sample of a healthy referral cache, for a root with two targets and a folder (link) with one:</p>
<pre><code class="language-text">Entry: \contoso.com\dfsroot
ShortEntry: \contoso.com\dfsroot
Expires in 300 seconds
UseCount: 0 Type:0x81 ( REFERRAL_SVC DFS )
    0:[\ROOTSERVER1\dfsshare] State:0x119 ( ACTIVE )
    1:[\ROOTSERVER2\dfsshare] State:0x09 ( )

Entry: \contoso.com\dfsroot\link
ShortEntry: \contoso.com\dfsroot\link
Expires in 1800 seconds
UseCount: 0 Type:0x1 ( DFS )
    0:[\fileserver\data] State:0x131 ( ACTIVE )
</code></pre>
<table>
<thead>
<tr>
<th>What the referral cache shows</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>No entry for the namespace</td>
<td>"If you cannot find an entry for the desired namespace, this is evidence that the domain controller did not return a referral."</td>
<td>Steps 3, 6</td>
</tr>
<tr>
<td>A root entry, no target marked ACTIVE</td>
<td>"If not any of the namespace targets that are listed are designated as ACTIVE, that indicates that all targets were unreachable."</td>
<td>Steps 3, 4</td>
</tr>
<tr>
<td>A root entry with an ACTIVE target, no entry for the folder</td>
<td>The root server didn't give out a folder referral, Microsoft's third cause (the pairing is our reading)</td>
<td>Steps 5, 7</td>
</tr>
<tr>
<td>A folder entry, but every target listed is down or in the wrong site</td>
<td>The ordering or the site mapping, not the DC (our reading)</td>
<td>Steps 7, 8</td>
</tr>
<tr>
<td>A folder entry whose ACTIVE target was removed from the namespace</td>
<td>A stale referral. Microsoft's defaults: root referrals "The default setting is 300 seconds (five minutes)." and folder referrals "The default setting is 1800 seconds (30 minutes)."</td>
<td>Wait it out, or Step 10</td>
</tr>
</tbody></table>
<p>The domain cache answers the first link. Microsoft: "The entries that are marked by a plus sign (+) are the domain controllers that are currently used by the client." If the domain cache shows no DC for your domain, the client never got as far as the namespace. That's DC discovery, and <code>nltest /dsgetdc:corp.contoso.com /force</code> is the quick read before you go further (our addition).</p>
<p>A Microsoft Q&amp;A thread reports cached domain-path entries that didn't expire after folder targets were changed, giving "Element Not Found" until the client restarted or the cache was flushed. That's a community report on Windows 10 and Server 2019, not a documented behavior, but it's a reason to read the cache before blaming the server.</p>
<p>If the cache doesn't settle it, Microsoft's guidance is a network capture on the client: filter <code>tcp.port==445 and DFSC</code>, because "The protocol is named DFSC by packet capture parsers." The client should send a DFS referral request and get a referral response. Microsoft: "If no DFS referral is being sent, and there's no indication of a firewall or anti-virus block, restart the DFS client to clear any DFS referral caching."</p>
<hr />
<h2>Step 3: Connectivity to a DC and to each namespace server (read-only)</h2>
<p>Microsoft's first test is a plain SMB connection by IP address, so name resolution and referrals are out of the picture:</p>
<pre><code class="language-console">:: Read-only. The active DC from the domain cache, then each namespace server, by IP only (addresses are examples)
net view \\10.20.0.10
net view \\10.20.0.21
net view \\10.20.0.22
</code></pre>
<p>Microsoft: "A successful connection lists all shares that are hosted by the domain controller." For the namespace servers, it's specific about the form: "you must specify only the IP address of the server, and you must not include the namespace share". The reason: "Otherwise, you may unknowingly be referred to another DFS root server. If this occurs, you will receive misleading results."</p>
<p>Microsoft's port list for the DFS Namespaces service:</p>
<table>
<thead>
<tr>
<th>Application protocol</th>
<th>Protocol</th>
<th>Port (Microsoft)</th>
</tr>
</thead>
<tbody><tr>
<td>NetBIOS Datagram Service</td>
<td>UDP</td>
<td>138</td>
</tr>
<tr>
<td>NetBIOS Session Service</td>
<td>TCP</td>
<td>139</td>
</tr>
<tr>
<td>LDAP Server</td>
<td>TCP and UDP</td>
<td>389</td>
</tr>
<tr>
<td>SMB</td>
<td>TCP</td>
<td>445</td>
</tr>
<tr>
<td>RPC</td>
<td>TCP</td>
<td>135</td>
</tr>
<tr>
<td>Randomly allocated high TCP ports</td>
<td>TCP</td>
<td>49152-65535</td>
</tr>
</tbody></table>
<p>Microsoft's footnote on the first two rows: "The NETBIOS ports are optional and are not required when DFSN is using FQDN Server names."</p>
<pre><code class="language-powershell"># Read-only. TCP reachability from the client to a DC and to each namespace server (names are examples)
foreach ($server in 'DC01.corp.contoso.com', 'FS01.corp.contoso.com', 'FS02.corp.contoso.com') {
  foreach ($port in 445, 389, 135) {
    Test-NetConnection -ComputerName $server -Port $port -WarningAction SilentlyContinue |
      Select-Object ComputerName, RemotePort, TcpTestSucceeded
  }
}
</code></pre>
<p>Microsoft's <code>Test-NetConnection</code> page describes <code>-Port</code> as "the TCP port number on the remote computer," so this loop can't prove UDP 389. A working <code>nltest /dsgetdc /force</code> is the practical proof for the DC side (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>net view \\DC-IP</code> fails</td>
<td>The client can't reach the DC that would give the root referral</td>
<td>Firewall or routing; Step 6 for the DC</td>
</tr>
<tr>
<td>DC works, one namespace server fails on 445</td>
<td>That server is down, blocked, or its Server service is stopped</td>
<td>Step 5 for that root target</td>
</tr>
<tr>
<td>Every namespace server fails from one site only</td>
<td>A WAN or firewall change for that site</td>
<td>Network team, with Microsoft's port list</td>
</tr>
<tr>
<td>Everything answers by IP</td>
<td>Connectivity isn't the break</td>
<td>Steps 4-7</td>
</tr>
</tbody></table>
<p>Microsoft's older "How DFS Works" reference (written for Windows Server 2003) describes the client sending its domain, DC, and root referral requests to the IPC$ share of a DC or root server "in the context of the LocalSystem account". So a rule or policy that treats the computer differently from the user can break referrals while the user's own access looks fine (our reading).</p>
<hr />
<h2>Step 4: Name resolution for every server in the referral (read-only)</h2>
<p>Microsoft: "Clients must resolve the name of the DFS namespace and of any servers that are hosting the namespace." And: "By default, DFSN stores NetBIOS names for root servers." Read the names out of the Step 2 cache and resolve each one the way the client would:</p>
<pre><code class="language-powershell"># Read-only. Resolve the short and full names from the referral (FS01/FS02/BR-FS01 are examples)
foreach ($name in 'FS01', 'FS01.corp.contoso.com', 'FS02', 'BR-FS01', 'BR-FS01.corp.contoso.com') {
  Resolve-DnsName -Name $name -ErrorAction SilentlyContinue | Select-Object @{ n = 'Asked'; e = { $name } }, Name, Type, IPAddress
}
</code></pre>
<pre><code class="language-console">:: Read-only. The NetBIOS name cache and the DNS resolver cache
nbtstat -c
ipconfig /displaydns
</code></pre>
<p>Microsoft's <code>nbtstat</code> reference: <code>-c</code> "Displays the contents of the NetBIOS name cache, the table of NetBIOS names and their resolved IP addresses." Then read how the namespace servers answer:</p>
<pre><code class="language-powershell"># Read-only. Does each namespace server hand out FQDN or NetBIOS names in referrals?
foreach ($s in 'FS01', 'FS02') { Get-DfsnServerConfiguration -ComputerName $s | Select-Object ComputerName, UseFqdn, EnableInsiteReferrals, EnableSiteCostedReferrals, SyncIntervalSec }
</code></pre>
<p>Microsoft on <code>UseFqdn</code>: "The default for DFS namespace servers is to use NetBIOS names in referrals." Its KB 244380 explains who that bites: "some clients don't use NetBIOS. Two examples are clients that aren't running Windows and clients that operate in an environment without WINS or that use DNS name suffixes." And: "In these cases, the client may be unable to resolve the server name that is returned from the root referral query."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Short name fails, FQDN resolves</td>
<td>No WINS and no DNS suffix that completes the short name</td>
<td>DNS suffix search list first; FQDN referrals in Step 10</td>
</tr>
<tr>
<td>Short name resolves to an old or wrong address</td>
<td>A stale DNS or WINS record for a re-addressed server</td>
<td>Fix the record; <code>ipconfig /displaydns</code> shows what the client cached</td>
</tr>
<tr>
<td>Only VPN clients fail</td>
<td>The tunnel's DNS settings don't complete short names, or don't reach AD DNS</td>
<td>VPN DNS suffix and servers</td>
</tr>
<tr>
<td>Non-Windows clients fail, Windows clients work</td>
<td>Microsoft's KB 244380 case</td>
<td>FQDN referrals (Step 10)</td>
</tr>
<tr>
<td>Everything resolves correctly</td>
<td>Name resolution isn't the break</td>
<td>Steps 5-7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: The namespace itself (read-only)</h2>
<p>Read the namespace, its root targets, the failing folder, and that folder's targets. Run these from a server with the DFS Management Tools; the names are examples:</p>
<pre><code class="language-powershell"># Read-only. Root settings, root targets, every folder, and the failing folder's targets
Get-DfsnRoot -Path '\\corp.contoso.com\Shares' | Format-List Path, Type, State, Flags, TimeToLiveSec
Get-DfsnRootTarget -Path '\\corp.contoso.com\Shares' | Select-Object TargetPath, State, ReferralPriorityClass, ReferralPriorityRank
Get-DfsnFolder -Path '\\corp.contoso.com\Shares\*' | Format-List
Get-DfsnFolderTarget -Path '\\corp.contoso.com\Shares\Finance' | Select-Object TargetPath, State, ReferralPriorityClass, ReferralPriorityRank

# Read-only. The DFS Namespace service on each namespace server and DC (WinRM; the remoting form is ours)
Invoke-Command -ComputerName FS01, FS02, DC01, DC02 -ScriptBlock { Get-Service -Name Dfs | Select-Object Name, Status, StartType }
</code></pre>
<p>The property names come from Microsoft's <code>Get-DfsnRoot</code> and <code>Get-DfsnFolderTarget</code> examples. KB 975440's first configuration check: "First, verify that the DFS service is started on all domain controllers and on DFS namespace/root servers." Then Microsoft's own namespace checks:</p>
<pre><code class="language-console">:: Read-only. Service and registry consistency across namespace servers, then metadata integrity, then the referral itself
dfsdiag /testdfsconfig /DFSroot:\\corp.contoso.com\Shares
dfsdiag /testdfsintegrity /DFSRoot:\\corp.contoso.com\Shares /recurse /full
dfsdiag /testreferral /DFSpath:\\corp.contoso.com\Shares\Finance
</code></pre>
<p>Microsoft's <code>dfsdiag</code> reference: <code>/testdfsconfig</code> "Verifies that the DFS registry configuration is consistent among namespace servers." <code>/testdfsintegrity</code> "Checks for DFS metadata corruption or inconsistencies between domain controllers." and "Detects overlapping DFS folders (links), duplicate folders, and folders with overlapping folder targets." For a folder path, <code>/testreferral</code> also validates "the site configuration for folder targets" and "the site association of the local host."</p>
<p>On each namespace server, read the root share and the registry entry that ties the server to the root:</p>
<pre><code class="language-powershell"># Read-only. The root share, its share permissions, and the root's registry entry (domain-based, Windows Server 2008 mode)
Get-SmbShare -Name Shares | Select-Object Name, Path, Description
Get-SmbShareAccess -Name Shares
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Dfs\Roots\domainV2\Shares' -ErrorAction SilentlyContinue | Select-Object LogicalShare, RootShare
</code></pre>
<p>Microsoft's recovery article says the shares named by the <code>LogicalShare</code> and <code>RootShare</code> values "must exist and be accessible for proper operation of a DFS root." And: "Access will be denied to a root if the share is missing or is configured with inappropriate permissions." Stand-alone roots live under <code>Roots\Standalone</code> instead, and Windows 2000 Server mode roots under <code>Roots\Domain</code>.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Root <code>State</code> is Offline</td>
<td>Set a root to Offline and "the entire namespace becomes inaccessible."</td>
<td>Find out who set it and why; Step 10</td>
</tr>
<tr>
<td>A root target or folder target <code>State</code> is Offline</td>
<td>"Clients do not receive referrals for a DFS namespace target that is offline."</td>
<td>Was it maintenance? Step 10</td>
</tr>
<tr>
<td><code>Dfs</code> stopped on one namespace server</td>
<td>That root target can't give referrals</td>
<td>Step 9 (the RPC rows) and Step 10</td>
</tr>
<tr>
<td>Only one root target</td>
<td>Microsoft calls a single namespace server for a domain-based root "not recommended"</td>
<td>Plan a second one after the fix</td>
</tr>
<tr>
<td>Root share missing, or share permissions changed</td>
<td>Access to the root is denied</td>
<td>Restore the share before any namespace change</td>
</tr>
<tr>
<td>No <code>domainV2\Shares</code> key on a root target</td>
<td>That server doesn't know it hosts the root. DFS Management reports "Element not found"</td>
<td>Step 9</td>
</tr>
<tr>
<td><code>/testdfsintegrity</code> reports overlapping or duplicate folders</td>
<td>Metadata problem, not the client</td>
<td>Fix in DFS Management, one folder at a time</td>
</tr>
</tbody></table>
<hr />
<h2>Step 6: AD and the PDC emulator: is the metadata current? (read-only)</h2>
<p>For a domain-based namespace, the configuration lives in AD. Microsoft: "When an administrator makes a change to the domain-based namespace, the change is made on the Primary Domain Controller (PDC) emulator master." And: "Domain controllers and DFS root servers periodically poll PDC for configuration information." With root scalability mode on, the root servers read from a nearby DC instead. Microsoft's description of that mode: it "reduces the load on the Primary Domain Controller (PDC) Emulator, but increases the time required for changes to the namespace to replicate to all namespace servers. Until changes replicate to all servers, users might have an inconsistent view of the namespace."</p>
<pre><code class="language-powershell"># Read-only. Who holds the PDC emulator role, and can each namespace server reach it on TCP 389/445?
$pdc = (Get-ADDomain -Identity corp.contoso.com).PDCEmulator
$pdc
Invoke-Command -ComputerName FS01, FS02 -ScriptBlock {
  param($pdc)
  foreach ($port in 389, 445) { Test-NetConnection -ComputerName $pdc -Port $port -WarningAction SilentlyContinue | Select-Object ComputerName, RemotePort, TcpTestSucceeded }
} -ArgumentList $pdc

# Read-only. The PDC emulator's own SRV record, which DFS Management looks up
Resolve-DnsName -Name '_ldap._tcp.pdc._msdcs.corp.contoso.com' -Type SRV -DnsOnly
</code></pre>
<pre><code class="language-console">:: Read-only. Microsoft's replication check from KB 975440, then dfsdiag's DC checks
repadmin /showrepl * DC=corp,DC=contoso,DC=com
dfsdiag /testdcs /domain:corp.contoso.com
</code></pre>
<p>Microsoft on the <code>repadmin</code> output: "Review the status and time of the last successful replication to make sure that DFSN configuration changes have reached all domain controllers." And <code>dfsdiag /testdcs</code> checks on each DC that the DFS Namespace service runs with an Automatic startup type, plus site-costed referral support for NETLOGON and SYSVOL. That DC check matters for more than file shares: Microsoft's port reference says "The DFSN service is required for Active Directory domain controllers to advertise the SYSVOL shared folder."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Namespace servers can't reach the PDC emulator on 389</td>
<td>They keep serving the metadata they last read, so recent changes never show up</td>
<td>Network path to the PDC; Step 9 rows for 389</td>
</tr>
<tr>
<td><code>repadmin</code> shows inbound replication failures to some DCs</td>
<td>Those DCs give clients root referrals from old metadata (Microsoft: replication failures "may prevent servers from issuing correct referrals")</td>
<td>Fix AD replication first</td>
</tr>
<tr>
<td>Root scalability on, recent change missing on some servers</td>
<td>Expected lag per Microsoft's text above</td>
<td>Wait, or confirm the DC those servers poll is current</td>
</tr>
<tr>
<td><code>Dfs</code> not Automatic or not running on a DC</td>
<td>That DC can't hand out domain or root referrals</td>
<td>Step 10, on that DC</td>
</tr>
<tr>
<td>PDC SRV record missing</td>
<td>DFS Management fails with "The specified domain either does not exist or cannot be contacted" (Step 9)</td>
<td>Restart Netlogon on the PDC (Step 10)</td>
</tr>
</tbody></table>
<p>Two caches sit between a change and the client. Microsoft's older "How DFS Works" reference (Windows Server 2003 era) says domain controllers "store domain-based root referrals in a memory cache for 15 minutes", and that "Restarting the DFS service on the domain controller will cause this cache to be cleared." Then the client keeps its own copy for the TTL from Step 2. So a root target added at 9:00 may not reach every client until well after 9:15 (our arithmetic from those defaults).</p>
<hr />
<h2>Step 7: Sites and referral ordering (read-only)</h2>
<p>This is the step the BR1 ticket lands on. Microsoft's ordering article: "Targets on the client's site are always listed first in a referral." Targets outside the site follow by the ordering method: random order, lowest cost, or exclude targets outside of the client's site. On that last one: "If no same-site targets exist, the client does not receive a referral and cannot access that portion of the namespace."</p>
<p>How DFS decides the sites, from KB 975440: "The DFSN service maps the client to a site by analyzing the source IP address of the client's referral request." And: "The DFS service also maps each root target server to a site by resolving the target server's name to an IP address." It names the failure directly: "One common scenario in which this occurs is a client that belongs to a site that contains no namespace or folder targets."</p>
<pre><code class="language-powershell"># Read-only. In-site and site-costing settings at every level: root, folder, and each namespace server
(Get-DfsnRoot -Path '\\corp.contoso.com\Shares').Flags
Get-DfsnFolder -Path '\\corp.contoso.com\Shares\Finance' | Format-List
foreach ($s in 'FS01', 'FS02') { Get-DfsnServerConfiguration -ComputerName $s | Select-Object ComputerName, EnableInsiteReferrals, EnableSiteCostedReferrals }

# Read-only. Is the client's new subnet in AD, and which site does each target's address map to?
Get-ADReplicationSubnet -Filter * | Select-Object Name, Site, Location | Sort-Object Name
Resolve-DnsName -Name 'BR-FS01.corp.contoso.com' -Type A -DnsOnly
</code></pre>
<pre><code class="language-console">:: Read-only. The client's AD site, then Microsoft's site checks for the folder and the target server
nltest /dsgetsite
dfsdiag /testsites /DFSpath:\\corp.contoso.com\Shares\Finance /full
dfsdiag /testsites /machine:BR-FS01
</code></pre>
<p><code>Flags</code> lists the settings that are on (Microsoft's example shows <code>Site Costing</code>). <code>Set-DfsnRoot</code> names the five: site costing, in-site referrals, access-based enumeration, root scalability, and target failback. In-site referrals can be on for the root, for one folder, or for a whole namespace server (with <code>EnableInsiteReferrals</code> on a server, "the server returns only referrals for targets in the same site as the client."), so read all three. <code>dfsdiag /testsites</code> "Checks the configuration of active directory Domain Services (AD DS) sites by verifying that servers that act as namespace servers or folder (link) targets have the same site associations on all domain controllers."</p>
<p>Microsoft's namespace client FAQ lists why clients go to unexpected targets. The ones that show up most:</p>
<table>
<thead>
<tr>
<th>Cause from Microsoft's FAQ</th>
<th>What to read</th>
</tr>
</thead>
<tbody><tr>
<td>"The client's IP address is not in a subnet that is defined in Active Directory and so DFS cannot obtain site information about the client."</td>
<td><code>Get-ADReplicationSubnet</code> vs the client's address; <code>nltest /dsgetsite</code></td>
</tr>
<tr>
<td>"The target's IP address is not in a subnet that is defined in Active Directory and so DFS cannot obtain site information about the target."</td>
<td>The target server's address vs the subnet list</td>
</tr>
<tr>
<td>A server or DC moved in Sites and Services while its IP "still maps to the subnet of the previous site"</td>
<td><code>dfsdiag /testsites /machine:</code> for that server</td>
</tr>
<tr>
<td>"The client is using a cached referral that has become outdated due to target change, site changes, or both."</td>
<td>Step 2 cache; the TTL</td>
</tr>
<tr>
<td>"Site information has changed, but the old site information is still cached on the root server or domain controller in the target site cache, client site cache, or site cost cache."</td>
<td>Time since the site change</td>
</tr>
<tr>
<td>NAT or WAN appliances that "change the source IP address of packets sent from a client computer to match the subnet of another site"</td>
<td>Whether the branch traffic is NATed on the way in</td>
</tr>
</tbody></table>
<p>On that cache row, the older "How DFS Works" reference puts the default site cost cache at up to 12 hours on DCs and root servers, so a site-link cost change can take that long to show (Windows Server 2003-era default; check before you rely on it).</p>
<p>Two more behaviors explain "it went to HQ and stayed there." Clients stick: after a failover, a client keeps using the target it's on when the referral refreshes. Microsoft's FAQ: "if a client fails over from a local server to a remove server (such as when the local server fails), the client will continue to access the remote server, even after the local server is restored." (Microsoft's typo: <em>remove</em> for <em>remote</em>.) Target failback changes that: with it on, <code>Set-DfsnRoot</code> says "once the first server becomes available again, the client fails back to the first server." And priority overrides ordering: <code>ReferralPriorityClass</code> takes <code>GlobalHigh</code>, <code>SiteCostHigh</code>, <code>SiteCostNormal</code>, <code>SiteCostLow</code>, or <code>GlobalLow</code>, and targets set to first or last among all targets "are still listed in the referral, even if the ordering method is set to Exclude targets outside of the client's site."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>nltest /dsgetsite</code> empty or wrong on BR1 PCs; 10.40.30.0/24 not in the subnet list</td>
<td>DFS can't place the client. With in-site on, Microsoft's text says no referral</td>
<td>Step 10: add the subnet to the right site</td>
</tr>
<tr>
<td>In-site on for the folder, no target in the client's site</td>
<td>Microsoft's "common scenario"</td>
<td>Add a same-site target, or turn in-site off for that folder</td>
</tr>
<tr>
<td>Branch users get the HQ target first</td>
<td>Branch target in the wrong site, or the branch target is down, or a priority class says so</td>
<td><code>dfsdiag /testsites /machine:</code>; the priority columns from Step 5</td>
</tr>
<tr>
<td>Users stay on HQ after the branch server came back</td>
<td>Stickiness without failback</td>
<td>Target failback (Step 10)</td>
</tr>
<tr>
<td>Only after a site or subnet change</td>
<td>Cached site data on DCs and root servers</td>
<td>Time, or the DFS service restart on one server (Step 10)</td>
</tr>
</tbody></table>
<hr />
<h2>Step 8: The folder target itself (read-only)</h2>
<p>Once the referral is right, the rest is plain SMB to the target. Microsoft's guidance page: "SMB troubleshooting applies to fixing the file server connection." Test the target by its own path, as the user, from the affected PC:</p>
<pre><code class="language-powershell"># Read-only. The folder target directly, then its share and NTFS permissions (run the last two on BR-FS01)
Test-Path -Path '\\BR-FS01\Finance'
Get-SmbShareAccess -Name Finance
(Get-Acl -Path 'D:\Shares\Finance').Access | Select-Object IdentityReference, FileSystemRights, AccessControlType
</code></pre>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>\\BR-FS01\Finance</code> fails the same way</td>
<td>The target, not the namespace (our reading)</td>
<td>SMB and file-server troubleshooting</td>
</tr>
<tr>
<td>Users see the folder but get access denied</td>
<td>Share or NTFS permissions on the target. DFS folder permissions don't grant access</td>
<td>Fix permissions on the target share</td>
</tr>
<tr>
<td>Users can open a folder they shouldn't see listed, or can't see one they can open</td>
<td>Access-based enumeration settings (below)</td>
<td>ABE, not permissions</td>
</tr>
<tr>
<td>A NAS or non-Windows target fails from Windows 11 24H2 with "The cryptographic signature is invalid."</td>
<td>Microsoft: "Windows 11, version 24H2 Enterprise, Pro, and Education require both outbound and inbound SMB signing."</td>
<td>Enable signing on the device</td>
</tr>
<tr>
<td>A NAS target fails with "You can't access this shared folder because your organization's security policies block unauthenticated guest access."</td>
<td>The device only offers guest access</td>
<td>Give the device real accounts</td>
</tr>
<tr>
<td>Two targets, users see different files</td>
<td>Replication between the targets, not referrals</td>
<td><a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">The DFS Replication post</a></td>
</tr>
</tbody></table>
<p>On the two NAS rows, Microsoft's own caution: "We don't recommend disabling SMB signing as a workaround for third-party servers." And on guest logons: "We recommend that you don't enable insecure guest logons."</p>
<p><strong>Access-based enumeration (ABE) is display, not security.</strong> Microsoft: "Access-based enumeration does not prevent users from getting a referral to a folder target if they already know the DFS path." And: "DFS folder permissions are used only for displaying or hiding DFS folders, not for controlling access, making Read access the only relevant permission at the DFS folder level." When ABE seems broken, check Microsoft's prerequisite first: "To enable access-based enumeration on a namespace, all namespace servers must be running Windows Server 2008 or newer. Additionally, domain-based namespaces must use the Windows Server 2008 mode." A namespace that's still in Windows 2000 Server mode (its registry key under <code>Roots\Domain</code>, not <code>Roots\domainV2</code>) can't hide folders until it's migrated. ABE on the namespace also doesn't hide files inside the target; for that, Microsoft says to "enable access-based enumeration on each shared folder by using Share and Storage Management." Read the folder's view permissions with <code>Get-DfsnAccess -Path '\\corp.contoso.com\Shares\Finance'</code>. Microsoft also notes: "In some environments, enabling access-based enumeration can cause high CPU utilization on the server and slow response times for users."</p>
<p><strong>Multiple read/write targets for fast-changing data.</strong> If Finance has a branch target and an HQ target kept in step by DFS Replication, and users get failed over between them, read Microsoft's support policy before you call it a namespace bug. It describes home folders and roaming profiles on branch and hub targets and says "the same issue applies to any fast-changing content that may not have been replicated to a replication partner when the referral redirects the user from a different partner to that partner." Its conclusion: "Therefore, they are not supported by Microsoft."</p>
<hr />
<h2>Step 9: When DFS Management says "The namespace cannot be queried" (read-only)</h2>
<p>This is the admin's view, and users may be fine while it fails. Microsoft keeps one article per reason text. The short version:</p>
<table>
<thead>
<tr>
<th>Reason after "The namespace cannot be queried."</th>
<th>Microsoft's causes</th>
<th>First read</th>
</tr>
</thead>
<tbody><tr>
<td>"The RPC server is unavailable"</td>
<td>DFS Namespace service stopped or not <code>Running</code>; TCP 445 to the namespace server blocked; no same-as-parent A records for the domain (third-party DNS); stand-alone <code>ID</code>/<code>Svc</code> values missing</td>
<td><code>Get-Service -Name Dfs</code> on the namespace server; TCP 445; <code>Resolve-DnsName corp.contoso.com</code></td>
</tr>
<tr>
<td>"The remote procedure call failed"</td>
<td>The service crashed or stopped responding after the console connected</td>
<td>The service's state and the System log on that server</td>
</tr>
<tr>
<td>"Element not found"</td>
<td>The root's registry key or its <code>LogicalShare</code>/<code>RootShare</code> values missing or corrupt; rarely, the PDC or DC unreachable on 389</td>
<td>The Step 5 registry read on each root target</td>
</tr>
<tr>
<td>"The specified domain either does not exist or cannot be contacted"</td>
<td>PDC or DC unreachable on TCP/UDP 389 (CLDAP unanswered); LDAP SRV records for the PDC missing</td>
<td>Step 6: PDC reach and <code>_ldap._tcp.pdc._msdcs</code></td>
</tr>
<tr>
<td>"The specified server cannot perform the requested operation"</td>
<td>PDC or DC unreachable on TCP/UDP 389, or down</td>
<td>Step 6</td>
</tr>
<tr>
<td>"The device is not ready for use"</td>
<td>Stand-alone <code>ID</code>/<code>Svc</code> values missing; or the server is still trying to reach the PDC on 389</td>
<td>Registry, then Step 6</td>
</tr>
<tr>
<td>"Access is denied" when creating a domain-based namespace</td>
<td>The admin account is in Protected Users (NTLM fails); or UNC hardening requires mutual authentication on the domain paths</td>
<td>Your account's groups; <code>gpresult /h</code>; the HardenedPaths key</td>
</tr>
<tr>
<td>"The data is invalid", "The namespace has no targets", "Not enough memory resources", "The system cannot find the file specified"</td>
<td>Stand-alone root registry values corrupt, modified, or missing</td>
<td>The <code>Roots\Standalone</code> key on the root server</td>
</tr>
<tr>
<td>"Delegation information for the namespace cannot be queried. The request is not supported"</td>
<td>Rare. Microsoft says to investigate with simultaneous traces</td>
<td>Capture on the console machine and the namespace server</td>
</tr>
</tbody></table>
<p>Every one of those articles carries the same note: "After you apply the solutions, remove the DFS Namespace from the DFS Management console and add it back, or close and reopen the console to make the changes take effect." So reopen the console before you decide a fix didn't work.</p>
<p>On the "same as parent" row, Microsoft is clear that those A records "aren't a requirement and are only used for DNS clients that don't understand SRV records," and that the hosts-file entry it describes is "As an exceptional workaround for exceptional situations" on the machine running the console, not for clients. On the Access is denied row, Microsoft's fixes are to take the account out of Protected Users or to set <code>RequireMutualAuthentication</code> to 0 for the domain's hardened paths. Both relax a protection, so we'd create the namespace with an admin account that isn't in Protected Users and leave UNC hardening alone (our view).</p>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Deleting and recreating the namespace</strong></td>
<td>Every folder, target, priority, and permission has to come back exactly. If the break was a subnet, a target state, or a port, the new namespace fails the same way (our reading)</td>
<td>Steps 2-7: find the layer; export first (Step 10)</td>
</tr>
<tr>
<td><strong><code>dfsutil /clean</code> on a working root server</strong></td>
<td>Microsoft's cleanup steps "should only be used if recovery of the configuration data is not possible or is not desired." and "These changes are not recoverable unless you make a backup of the system state for the domain controller or for the namespace server."</td>
<td>Step 9 tables; Microsoft's recovery article</td>
</tr>
<tr>
<td><strong>Editing the <code>Dfs-Configuration</code> objects in ADSI Edit</strong></td>
<td>Microsoft on the namespace's AD object: "No parts of this object should ever be modified directly."</td>
<td>The DFSN cmdlets or DFS Management</td>
</tr>
<tr>
<td><strong>Hand-editing the <code>Dfs\Roots</code> registry</strong></td>
<td>Microsoft: "It is recommended to never directly edit these registry values."</td>
<td>Import the key from a healthy root target (Step 10), if Step 9 says it's missing</td>
</tr>
<tr>
<td><strong>DCs in every client's hosts file</strong></td>
<td>Microsoft's hosts entry is "As an exceptional workaround for exceptional situations" on the console machine. It pins clients to addresses that will change (our reading)</td>
<td>Step 4 and Step 6: fix DNS</td>
</tr>
<tr>
<td><strong>Flushing every cache before reading it</strong></td>
<td>The cache is the evidence for Step 2. After a flush you can't tell which referral the client had (our reading)</td>
<td>Read <code>dfsutil cache referral</code> first; flush on one PC as a test</td>
</tr>
<tr>
<td><strong>Turning on in-site referrals to keep users local</strong></td>
<td>With no target in a site, Microsoft says the client "does not receive a referral" for that part of the namespace</td>
<td>Lowest-cost ordering plus target priority</td>
</tr>
<tr>
<td><strong>Disabling SMB signing or enabling guest logons for a NAS target</strong></td>
<td>Microsoft recommends against both</td>
<td>Enable signing and real accounts on the device</td>
</tr>
<tr>
<td><strong>Treating ABE as access control</strong></td>
<td>Microsoft: ABE "does not prevent users from getting a referral to a folder target if they already know the DFS path."</td>
<td>Permissions on the target share and folder</td>
</tr>
<tr>
<td><strong>Stopping DFS Namespaces on a DC to simplify things</strong></td>
<td>Microsoft: "The DFSN service is required for Active Directory domain controllers to advertise the SYSVOL shared folder."</td>
<td>Leave it running and Automatic on every DC</td>
</tr>
</tbody></table>
<hr />
<h2>Step 10: Change one layer, then prove it (only after the read)</h2>
<p>Pick the one change Steps 1-9 point at. Save the configuration first. Microsoft's export command for a domain-based namespace (Windows Server 2008 and later form):</p>
<pre><code class="language-console">:: Low-risk write. Export the namespace metadata and the DFS registry before any change (writes local files)
dfsutil root export \\corp.contoso.com\Shares C:\Temp\Shares-dfs-root.txt
reg export HKLM\SOFTWARE\Microsoft\Dfs\Roots C:\Temp\dfs-roots-before.reg /y
</code></pre>
<p>Microsoft's recovery article says the export "may be used to recreate the missing DFS configuration information lost through accidental deletion." The <code>reg export</code> line is our habit. For a full restore point, Microsoft says "To back up a DFS Namespace server, a system-state backup is required." (<a href="https://dev.to/adminpackstudio/windows-server-backup-failed-a-read-first-event-id-vss-writer-shadow-storage-and-efi-lock-triage-572g">Windows Server Backup Failed</a> covers the backup side if that job fails.)</p>
<p><strong>A client subnet that isn't in AD (Step 7, the BR1 ticket).</strong> Map the subnet to its site. The subnet, site, and location are examples:</p>
<pre><code class="language-powershell"># Change. Map the re-addressed branch subnet to its site. Dry-run first
New-ADReplicationSubnet -Name '10.40.30.0/24' -Site BR1 -Location 'Branch 1' -WhatIf
</code></pre>
<p>After AD replication, the namespace servers and DCs still hold cached site data (Step 6 and Step 7), so give it time before you test, and test on one PC.</p>
<p><strong>In-site referrals with no target in the site (Step 7).</strong> Either add a target in that site, or turn in-site off for that folder so clients fall through to the ordering method:</p>
<pre><code class="language-powershell"># Change. Option A: let the folder refer outside the client's site. Dry-run first
Set-DfsnFolder -Path '\\corp.contoso.com\Shares\Finance' -EnableInsiteReferrals $false -WhatIf

# Change. Option B: keep HQ's copy as the last resort for everyone (Microsoft's GlobalLow class). Dry-run first
Set-DfsnFolderTarget -Path '\\corp.contoso.com\Shares\Finance' -TargetPath '\\FS01\Finance' -ReferralPriorityClass GlobalLow -WhatIf
</code></pre>
<p><strong>A target set offline (Step 5).</strong> Bring it back only after you know why it was offline:</p>
<pre><code class="language-powershell"># Change. Re-enable referrals to one root target or one folder target. Dry-run first
Set-DfsnRootTarget -Path '\\corp.contoso.com\Shares' -TargetPath '\\FS02\Shares' -State Online -WhatIf
Set-DfsnFolderTarget -Path '\\corp.contoso.com\Shares\Finance' -TargetPath '\\BR-FS01\Finance' -State Online -WhatIf
</code></pre>
<p><strong>Users stuck on the HQ target after the branch came back (Step 7).</strong> Turn on target failback, at the root or for one folder:</p>
<pre><code class="language-powershell"># Change. Clients fail back to the preferred target after their referral expires. Dry-run first
Set-DfsnFolder -Path '\\corp.contoso.com\Shares\Finance' -EnableTargetFailback $true -WhatIf
</code></pre>
<p><strong>The DFS Namespace service stopped on a namespace server or DC (Steps 5, 9).</strong> Microsoft's commands are <code>Get-Service -Name Dfs</code> and <code>Start-Service -Name Dfs</code>. If it won't start, Microsoft points at "a registry corruption or a service dependency issue," so read the dependencies before you retry:</p>
<pre><code class="language-powershell"># Change, on FS02 only. Dry-run first
Start-Service -Name Dfs -WhatIf
</code></pre>
<p><strong>The PDC's SRV records missing (Steps 6, 9).</strong> Microsoft: "Restarting the Netlogon service on domain controllers should repopulate all missing SRV records under the _msdcs.contoso.com zone." Do it on the DC with the missing records:</p>
<pre><code class="language-powershell"># Change, on DC01. Clients fail over to other DCs briefly (our note). Dry-run first
Restart-Service -Name Netlogon -WhatIf
</code></pre>
<p><strong>A root target's registry key missing ("Element not found", Step 9).</strong> Microsoft's fix for a domain-based root is to import the key "from any other DFS root server holding the same DFS Namespace root or a valid registry backup", then "Restart the DFS server or the DFS server service so that the changes in the registries are loaded into memory again." <code>reg import</code> has no dry run, so export from the healthy server, compare the file, then import (the commands are ours):</p>
<pre><code class="language-console">:: Change. On FS01 (healthy): export the root's key. On FS02 (broken): import it, then restart the DFS Namespace service
reg export "HKLM\SOFTWARE\Microsoft\Dfs\Roots\domainV2\Shares" C:\Temp\Shares-domainV2.reg /y
reg import C:\Temp\Shares-domainV2.reg
</code></pre>
<p><strong>Short names that clients can't resolve (Step 4).</strong> Fix the DNS suffix search list first. Microsoft's server-side answer is FQDN referrals, and its KB 244380 runs it as a sequence per namespace server: remove that server's domain-based root targets (only when another namespace server hosts each namespace: "you should make sure that there is in fact more than one namespace server that is hosting your namespace"), set <code>UseFqdn</code>, restart the service with <code>Stop-Service dfs; Start-Service dfs</code>, re-add the root targets, then update folder targets to FQDNs with <code>dfsutil root export</code> / <code>dfsutil root import set</code>. The one setting, dry-run:</p>
<pre><code class="language-powershell"># Change, one namespace server at a time, inside Microsoft's KB 244380 sequence. Dry-run first
Set-DfsnServerConfiguration -ComputerName FS01 -UseFqdn $true -WhatIf
</code></pre>
<p><strong>A stale referral on one client (Step 2).</strong> Microsoft's FAQ: "You can manually clear the referral cache by using the dfsutil cache referral flush command." Flush one PC, test, then decide whether the rest need it or can wait out the TTL:</p>
<pre><code class="language-console">:: Change, on one client. Then reproduce
dfsutil cache referral flush
</code></pre>
<p><strong>Then prove it from the affected PC.</strong> Read the result the same way you read the failure:</p>
<pre><code class="language-powershell"># Read-only. The namespace path, the referral the client now holds, and Microsoft's referral test
Test-Path -Path '\\corp.contoso.com\Shares\Finance'
dfsutil cache referral
dfsdiag /testreferral /DFSpath:\\corp.contoso.com\Shares\Finance
nltest /dsgetsite
</code></pre>
<p>The folder entry should list the BR1 target first and ACTIVE, and <code>nltest /dsgetsite</code> should return BR1. Check again after the next referral expiry (30 minutes for folders by default), because a fix that only works until the cache refreshes isn't a fix (our habit).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Client subnet not in AD</td>
<td><code>New-ADReplicationSubnet</code> for that subnet and site</td>
<td>Hard-coding a folder target in a drive map</td>
</tr>
<tr>
<td>In-site on, no target in the site</td>
<td>A same-site target, or in-site off for that folder</td>
<td>In-site on the whole namespace server</td>
</tr>
<tr>
<td>Target offline on purpose, maintenance over</td>
<td><code>Set-DfsnFolderTarget -State Online</code></td>
<td>Recreating the folder</td>
</tr>
<tr>
<td>Users stuck on the remote target</td>
<td>Target failback on the folder or root</td>
<td>Flushing every client's cache daily</td>
</tr>
<tr>
<td>Short names don't resolve</td>
<td>DNS suffix search list, then Microsoft's FQDN referral sequence</td>
<td>Hosts files on clients</td>
</tr>
<tr>
<td>Namespace servers can't reach the PDC</td>
<td>Fix the path to the PDC on 389/445</td>
<td>Root scalability as a workaround</td>
</tr>
<tr>
<td>Root target registry key missing</td>
<td>Import from a healthy root target, then restart the service</td>
<td><code>dfsutil /clean</code> and recreate</td>
</tr>
<tr>
<td>NAS target fails on Windows 11 24H2</td>
<td>SMB signing on the device</td>
<td>Disabling signing on clients</td>
</tr>
<tr>
<td>"Access is denied" creating a namespace</td>
<td>An admin account outside Protected Users</td>
<td>Turning off UNC hardening</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does "DFS namespace not accessible" mean?</strong>
The client couldn't finish the chain of referrals for the path. Microsoft's three causes: it couldn't get a namespace referral from a domain controller, it couldn't connect to a namespace server, or the namespace server didn't give out a folder referral. The client's referral cache (<code>dfsutil cache referral</code>) shows which one.</p>
<p><strong>Why can I open <code>\\server\share</code> but not the namespace path?</strong>
Because the direct path skips the referral layer. When the file server works by name, read the referral cache, the client's AD site, and the namespace settings (target state, in-site referrals, priorities) before you touch the file server.</p>
<p><strong>Why does the namespace work at HQ but not at a branch or over VPN?</strong>
Usually sites or names. DFS maps the client to a site from the source IP of its referral request. A branch or VPN subnet that isn't in AD Sites and Services, combined with in-site referrals, means no referral at all. A VPN client that can't resolve short server names fails on the default NetBIOS-name referrals.</p>
<p><strong>How do I clear the DFS referral cache?</strong>
<code>dfsutil cache referral flush</code> on the client, or restart it. <code>dfsutil</code> comes with the DFS Management Tools (RSAT). Read the cache before you flush it, because it's your evidence of which referral the client had.</p>
<p><strong>What does "The namespace cannot be queried" mean?</strong>
DFS Management couldn't read the namespace. The reason after the message points at the layer: "The RPC server is unavailable" usually means the DFS Namespace service or TCP 445 to that server, "The specified domain either does not exist or cannot be contacted" means the PDC or its SRV records, and "Element not found" usually means the root's registry entry on a namespace server.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication Not Working</a> (when the referral is right but two folder targets don't hold the same files)</li>
<li><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> (the drive map or GPO that points users at the namespace path)</li>
<li><a href="https://dev.to/adminpackstudio/windows-server-backup-failed-a-read-first-event-id-vss-writer-shadow-storage-and-efi-lock-triage-572g">Windows Server Backup Failed</a> (the system-state backup Microsoft names as the namespace server's restore point)</li>
</ul>
<h2>Free checklist</h2>
<p>A folder that works from HQ and fails from the branch is a change nobody tested from where the users sit. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dfs-namespace-not-accessible">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dfs-namespace-not-accessible</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dfs-namespace-not-accessible">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=dfs-namespace-not-accessible</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-9 favor read-only checks: <code>dfsutil cache referral</code> / <code>cache domain</code>, <code>net view</code> by IP, <code>Test-NetConnection</code>, <code>Resolve-DnsName</code>, <code>nbtstat -c</code>, <code>ipconfig /displaydns</code>, <code>Get-DfsnRoot</code>, <code>Get-DfsnRootTarget</code>, <code>Get-DfsnFolder</code>, <code>Get-DfsnFolderTarget</code>, <code>Get-DfsnServerConfiguration</code>, <code>Get-DfsnAccess</code>, <code>Get-Service</code>, <code>Get-SmbShare</code>, <code>Get-SmbShareAccess</code>, <code>Get-Acl</code>, <code>Get-ItemProperty</code>, <code>Get-ADDomain</code>, <code>Get-ADReplicationSubnet</code>, <code>nltest /dsgetsite</code> / <code>/dsgetdc</code>, <code>repadmin /showrepl</code>, and <code>dfsdiag /testdfsconfig</code> / <code>/testdfsintegrity</code> / <code>/testreferral</code> / <code>/testdcs</code> / <code>/testsites</code>. Installing the DFS Management Tools on a client is a small change; <code>dfsutil root export</code> and <code>reg export</code> write local files. New AD subnets, in-site and failback settings, target priority and state, starting or restarting services, registry imports, FQDN referrals, and <code>dfsutil cache referral flush</code> are deliberate changes. Change one layer at a time, test on one machine first, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Netlogon Event ID 5719: A Read-First Boot Timing, DNS, DC Locator, and Site Triage]]></title><description><![CDATA[Netlogon event ID 5719? Read the reason text, boot timing, DNS SRV records, DC locator, site, and ports before you rejoin or reset.]]></description><link>https://adminpackstudio.hashnode.dev/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/netlogon-event-id-5719-a-read-first-boot-timing-dns-dc-locator-and-site-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Active Directory]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[networking]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 06:40:09 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "PC-ACC-114 logs NETLOGON event 5719 every morning: 'This computer was not able to set up a secure session with a domain controller in domain CONTOSO due to the following: There are currently no logon servers available to service the logon request.' Group Policy logged 1129 at the same minute, and the startup script that maps the scanner share didn't run. The user signs in fine with cached credentials. A forum thread says to rejoin the domain, run <code>Test-ComputerSecureChannel -Repair</code> on every PC that logs 5719, and turn on 'Always wait for the network' for the whole domain. Can we do that today?"</p>
<p>Not yet. <strong>Event 5719 means Netlogon couldn't find or reach a domain controller at that moment. The reason text and the timing tell you which layer failed.</strong> Microsoft's article for this event lists the first cause as "The Netlogon service starts before the network is ready." It also says: "If you can log on to the domain without a problem, you can safely ignore event ID 5719." A rejoin or a secure channel reset changes the computer account. Neither one makes the network adapter come up faster or fixes DNS. Work through it as a chain: <strong>the exact reason text, when it fires, and what role the machine has</strong> → <strong>the events and the Netlogon log around it</strong> → <strong>was the network ready when Netlogon started (link, DHCP, 802.1X, NAC, driver)?</strong> → <strong>DNS: can this machine's DNS servers answer the DC locator's SRV queries?</strong> → <strong>DC locator and the AD site: which DC, from which site, with or without the cache?</strong> → <strong>the ports between this machine and the DC</strong> → <strong>the DC side: Netlogon running, not paused, and its records in DNS</strong> → <strong>the Netlogon service on this machine, and the Windows 11 24H2 "internal error" pattern</strong>. <code>Get-WinEvent</code>, the Netlogon debug log, <code>Get-NetAdapter</code>, <code>Get-NetConnectionProfile</code>, <code>ipconfig /all</code>, <code>Resolve-DnsName</code>, <code>nltest /dsgetdc</code>, <code>nltest /dsgetsite</code>, <code>Test-NetConnection</code>, <code>dcdiag</code>, and <code>sc qc netlogon</code> are the sources for that chain. Rejoin a PC whose switch port takes 30 seconds to forward traffic, and it logs the same 5719 on the next boot.</p>
<p>If the machine finds a DC and the secure channel itself is broken (event 3210, <code>nltest /sc_query</code> returning <code>ERROR_ACCESS_DENIED</code>, "the trust relationship between this workstation and the primary domain failed"), read <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">Trust Relationship Between This Workstation and the Primary Domain Failed</a>. It covers the secure channel and machine password repair side, so this post doesn't repeat it. If the 5719 matters because startup Group Policy didn't apply, <a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> covers the GPO side. This post covers <strong>"a domain-joined Windows client, member server, or domain controller logs NETLOGON 5719 ('This computer was not able to set up a secure session with a domain controller in domain...'), at boot, at random, or after an upgrade, and you need a read-first path through boot timing, DNS, DC location, sites, ports, the DC side, and the Netlogon service before you rejoin, reset the channel, or change Group Policy wait settings."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>5719 at boot with a normal sign-in is often harmless.</strong> Microsoft: "If you can log on to the domain without a problem, you can safely ignore event ID 5719." It matters when startup Group Policy, startup scripts, or sign-in also fail.</li>
<li><strong>Read the reason text.</strong> "There are currently no logon servers available" points at finding a DC. "An internal error occurred" on Windows 11 24H2 has its own pattern (Step 8). An access-denied reason points at the secure channel (<a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">the trust relationship post</a>). These pairings are our reading.</li>
<li><strong>Boot timing is Microsoft's first cause.</strong> "Some network adapters and switches have link arbitration and MAC address uniqueness checks that take longer to complete than the wait time that is set for Netlogon to detect network connectivity." Microsoft's first fix: a current NIC driver, or "enable the PortFast option on the network switches."</li>
<li><strong>On Windows 10 and later, the boot race may not log 5719 at all.</strong> Microsoft: "In Windows 10 and later versions, event 5719 is no longer logged in this situation." On a Windows 11 PC, a 5719 at every boot deserves a look at the reason text before you blame timing (our reading).</li>
<li><strong>Discovery is DNS plus a UDP LDAP ping.</strong> Microsoft: "UDP/389: This port is required to discover AD services." Check DNS and that port before you touch the computer account.</li>
<li><strong><code>nltest /sc_verify</code> isn't a read.</strong> Microsoft: if the secure channel doesn't work, "this parameter removes the existing channel, and then builds a new one."</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Reason text, timing, role, and OS version written down</td>
<td>"5719 again" with nothing else</td>
</tr>
<tr>
<td>2. Events and Netlogon log</td>
<td>5719 lined up against boot time, 1129, and the Netlogon log</td>
<td>5719s nobody compared with the boot time</td>
</tr>
<tr>
<td>3. Network ready at boot</td>
<td>Link, IP address, and domain network profile up before Netlogon gives up</td>
<td>5719 and 1129 at every boot, fine a minute later</td>
</tr>
<tr>
<td>4. DNS</td>
<td>Every DNS server the client uses can answer the <code>_msdcs</code> SRV queries</td>
<td>SRV lookups fail, or work on one DNS server only</td>
</tr>
<tr>
<td>5. DC locator and site</td>
<td><code>nltest /dsgetdc /force</code> returns a DC in the right site</td>
<td>Wrong site, no site, or a DC across the WAN</td>
</tr>
<tr>
<td>6. Ports</td>
<td>UDP 389 and 53, plus Kerberos, LDAP, and RPC to the DC</td>
<td>DNS works, the DC never answers</td>
</tr>
<tr>
<td>7. The DC side</td>
<td>Netlogon running and not paused on the DCs; SRV records registered</td>
<td>Every client in a site logs 5719 at once</td>
</tr>
<tr>
<td>8. Netlogon on this machine</td>
<td>Netlogon and its dependencies start; OS build current</td>
<td>Service Control Manager 7001; "An internal error occurred" on 24H2</td>
</tr>
<tr>
<td>9. Change</td>
<td>Only after Steps 1-8 agree: one layer, one change</td>
<td>Rejoined, reset, still logging 5719</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>The reason text after "due to the following:"</strong></td>
<td>Each reason points at a different layer (table below)</td>
</tr>
<tr>
<td><strong>When it fires</strong></td>
<td>At every boot, after resume or a network change, at random during the day, or after the Netlogon service restarts</td>
</tr>
<tr>
<td><strong>What else failed</strong></td>
<td>Sign-in, startup Group Policy (1129), startup scripts, drive maps, or nothing</td>
</tr>
<tr>
<td><strong>Role</strong></td>
<td>Client, member server, or domain controller. A DC sets up its secure session with the PDC emulator (Step 7)</td>
</tr>
<tr>
<td><strong>Wired, Wi-Fi, or VPN</strong></td>
<td>802.1X, NAC, and VPN timing each delay the first DC contact (Step 3)</td>
</tr>
<tr>
<td><strong>OS version and build</strong></td>
<td>The Windows 11 24H2 "internal error" pattern (Step 8)</td>
</tr>
<tr>
<td><strong>What changed?</strong></td>
<td>New switch or switch config, NIC driver, DHCP scope, DNS servers, a new subnet, an OS upgrade, a DC retired</td>
</tr>
</tbody></table>
<p>Then branch on the reason text and the timing:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>"There are currently no logon servers available to service the logon request." at boot only, sign-in fine</td>
<td>Netlogon looked for a DC before the network was ready</td>
<td>Steps 2, 3</td>
</tr>
<tr>
<td>The same text at random times during the day</td>
<td>DNS, the DC locator, ports, or a DC that stopped answering</td>
<td>Steps 4-7</td>
</tr>
<tr>
<td>"We can't sign you in with this credential because your domain isn't available."</td>
<td>Microsoft's sample of this text is for DCs whose Netlogon service is stopped. Read it as no DC answered</td>
<td>Steps 5-7</td>
</tr>
<tr>
<td>"An internal error occurred." on Windows 11 24H2 or later, even after restarting Netlogon</td>
<td>Not timing. The Microsoft Q&amp;A pattern in Step 8</td>
<td>Step 8</td>
</tr>
<tr>
<td>An access-denied reason, or event 3210 next to it</td>
<td>The DC was found. The secure channel is the problem</td>
<td>The <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">trust relationship post</a></td>
</tr>
<tr>
<td>Only on Wi-Fi or only on VPN</td>
<td>802.1X, NAC, or a tunnel that comes up after Netlogon looked</td>
<td>Step 3</td>
</tr>
<tr>
<td>Every machine in one site at once</td>
<td>A DC, a site, DNS, or a firewall change, not the clients</td>
<td>Steps 5-7</td>
</tr>
<tr>
<td>Logged on a domain controller</td>
<td>Its secure session to the PDC emulator, or its own DNS</td>
<td>Step 7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the events and the Netlogon log (read-only)</h2>
<p>Start with the System log. Line the 5719s up against the last boot, and read the other events in the same minute:</p>
<pre><code class="language-powershell"># Read-only. NETLOGON 5719s in the last 7 days, with the reason text
Get-WinEvent -FilterHashtable @{
  LogName      = 'System'
  ProviderName = 'NETLOGON'
  Id           = 5719
  StartTime    = (Get-Date).AddDays(-7)
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Message | Format-List

# Read-only. When did this boot start? (our helper)
(Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime

# Read-only. Group Policy 1129s and Service Control Manager start failures in the same window
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-GroupPolicy'; Id = 1129 } -MaxEvents 10 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Message
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'Service Control Manager'; Id = 7000, 7001, 7003 } -MaxEvents 10 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, Message
</code></pre>
<p>The 1129 text Microsoft documents: "The processing of Group Policy failed because of lack of network connectivity to a domain controller. This may be a transient condition." A 5719 and a 1129 within a minute or two of boot, then a clean background refresh later, is the boot-timing pattern (our reading).</p>
<p>One Microsoft detail changes how you read a Windows 10 or 11 machine. In the boot-timing scenario its KB describes: "In Windows 10 and later versions, event 5719 is no longer logged in this situation." The evidence moves to the Netlogon log and to "events by components, depending on the Domain Controller connectivity (such as Group Policy)." So a modern client can have the boot race with no 5719 at all, and a modern client with a 5719 at every boot may have something else (our reading).</p>
<p>When the events don't settle it, turn on the Netlogon debug log for one or two boots. This is a diagnostic change, not a fix. Microsoft's commands:</p>
<pre><code class="language-console">Nltest /DBFlag:2080FFFF
</code></pre>
<p>Microsoft: "It's typically unnecessary to stop and restart the Netlogon service for Windows Server 2012 R2 or later to enable Netlogon logging." The log is <code>%windir%\debug\netlogon.log</code>, and by default it rolls to <code>Netlogon.bak</code> at 20 MB. Reboot once, then read it:</p>
<pre><code class="language-powershell"># Read-only. The discovery and session-setup lines from Microsoft's samples (pattern is ours)
Select-String -Path "$env:windir\debug\netlogon.log" -Pattern 'NlDiscoverDc|cannot pick trusted DC|No IP addresses present|Winsock Addrs|Transport Added|Eventlog: 5719' |
  Select-Object -Last 40
</code></pre>
<p>What Microsoft's sample lines mean:</p>
<table>
<thead>
<tr>
<th>Line (from Microsoft's KB samples)</th>
<th>Read it as (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td><code>NlSessionSetup: Session setup: cannot pick trusted DC</code></td>
<td>Netlogon tried to set up its session and had no DC to use</td>
</tr>
<tr>
<td><code>No IP addresses present, skipping No DC event log</code></td>
<td>No IP address yet. On Windows 10 and later, this line takes the place of the 5719 (Microsoft)</td>
</tr>
<tr>
<td><code>Winsock Addrs: 10.1.1.80 (1) List used to be empty.</code></td>
<td>The address arrived after Netlogon started. Compare its time with the first failure</td>
</tr>
<tr>
<td><code>NlDiscoverDc: Cannot find DC.</code></td>
<td>Discovery failed. Steps 3-6 decide why</td>
</tr>
<tr>
<td><code>Transport Added (10.0.64.232)</code></td>
<td>Microsoft's sample annotates this line: "Getting a proper IP address takes &gt;15 seconds."</td>
</tr>
</tbody></table>
<p>Turn it off when you're done: <code>Nltest /DBFlag:0x0</code>. Microsoft also warns against enabling Netlogon logging "in policies that apply to all systems, such as the Default Domain Policy."</p>
<p>For the Group Policy side, Microsoft's troubleshooting guide turns on the Group Policy Service debug log with a <code>GPSvcDebugLevel</code> DWORD of <code>30002</code> (hex) under <code>HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Diagnostics</code>, written to <code>%windir%\debug\usermode\gpsvc.log</code> ("If the usermode folder doesn't exist, create it under %windir%\debug."). In Microsoft's 5719 KB sample, that log shows the wait it calculated (<code>Taking min of 776 and 30000</code>), then <code>NlaGetIntranetCapability returned Not Ready error</code>, then <code>There is no connectivity</code>. That sequence is the network missing the startup window.</p>
<hr />
<h2>Step 3: Was the network ready when Netlogon started? (read-only)</h2>
<p>Microsoft's causes for the boot-time 5719, from the KB:</p>
<table>
<thead>
<tr>
<th>Microsoft's cause</th>
<th>What to read</th>
</tr>
</thead>
<tbody><tr>
<td>"The Netlogon service starts before the network is ready."</td>
<td>The Netlogon log timeline in Step 2</td>
</tr>
<tr>
<td>Link arbitration and "MAC address uniqueness checks" on some adapters and switches take longer than Netlogon waits</td>
<td>NIC driver version; switch port spanning-tree settings (PortFast)</td>
</tr>
<tr>
<td>"Solutions that verify the health of the new network member delay the network connection" (NAP, NAC, DirectAccess)</td>
<td>When the port moves from the quarantine network to the production network</td>
</tr>
<tr>
<td>"The 802.1X authentication process delays connections to the domain controllers."</td>
<td>Wired or wireless 802.1X timing</td>
</tr>
<tr>
<td>"The client experiences a delay to retrieve an IP address from the DHCP server."</td>
<td>DHCP response time at boot</td>
</tr>
</tbody></table>
<p>Read the adapter, the address, and the network profile Windows assigned. Run it right after a boot that logged 5719, and again a few minutes later:</p>
<pre><code class="language-powershell"># Read-only. Adapter, link, and driver (the selected properties are ours)
Get-NetAdapter -Physical | Select-Object Name, InterfaceDescription, Status, LinkSpeed, DriverVersion, DriverDate

# Read-only. IP address, gateway, and DNS servers per interface
Get-NetIPConfiguration -Detailed

# Read-only. Did Windows recognize the domain network?
Get-NetConnectionProfile | Select-Object InterfaceAlias, Name, NetworkCategory, IPv4Connectivity
</code></pre>
<p><code>NetworkCategory</code> should read <code>DomainAuthenticated</code>, which Microsoft defines as "Networks at a workplace that are joined to a domain." A domain PC on its office network that shows <code>Public</code> or <code>Private</code> hasn't been able to reach a DC on that network, which lines up with the <code>NlaGetIntranetCapability</code> lines in Step 2 (our reading).</p>
<p>Then read the values Microsoft's KB names, so you know what someone already changed. A missing value means the default:</p>
<pre><code class="language-powershell"># Read-only. Netlogon, TCP/IP, and Group Policy values from Microsoft's 5719 KB (missing = not set)
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -ErrorAction SilentlyContinue |
  Select-Object ExpectedDialupDelay, NegativeCachePeriod, DynamicSiteName
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -ErrorAction SilentlyContinue |
  Select-Object ArpRetryCount, DisableDHCPMediaSense
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters' -ErrorAction SilentlyContinue |
  Select-Object DisabledComponents
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -ErrorAction SilentlyContinue |
  Select-Object GpNetworkStartTimeoutPolicyValue
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Group Policy\History' -ErrorAction SilentlyContinue |
  Select-Object AvgWaitTimeoutAtStartup, CurrentWaitAtStartup
</code></pre>
<p>The <code>Tcpip6</code> path for <code>DisabledComponents</code> is ours. Microsoft's KB names the value but not the key.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Address arrives several seconds after Netlogon's first try; same at every boot</td>
<td>The boot race. Microsoft's first resolution: "install the most current driver for the Gigabit network adapter. Or, enable the PortFast option on the network switches."</td>
<td>Step 9: driver or switch port first</td>
</tr>
<tr>
<td>Wired 802.1X or NAC on the port</td>
<td>The port isn't forwarding to the DCs yet when Netlogon looks</td>
<td>Step 9: NAC/firewall timing, then <code>ExpectedDialupDelay</code></td>
</tr>
<tr>
<td><code>DisabledComponents</code> is <code>0xfffffff</code></td>
<td>Microsoft: "system startup will be delayed for five seconds if IPv6 is disabled incorrectly by setting the DisabledComponents registry setting to a value of 0xfffffff. The correct value is 0xff."</td>
<td>Step 9</td>
</tr>
<tr>
<td><code>AvgWaitTimeoutAtStartup</code> set, no wait-time policy configured</td>
<td>Microsoft's KB on this: "the time-out is miscalculated, and the Group Policy Service doesn't wait for the default 30 seconds for the network to be available." Fixed in updates released from August 22, 2023 (client) and September 12, 2023 (Windows Server 2022 and 2019)</td>
<td>Install current updates, or configure the wait policy (Step 9)</td>
</tr>
<tr>
<td>Values already set by someone (<code>NegativeCachePeriod</code>, <code>ExpectedDialupDelay</code>)</td>
<td>A previous fix attempt. Find out why before you add another</td>
<td>Read the change record</td>
</tr>
<tr>
<td>Wi-Fi only, before sign-in</td>
<td>The wireless profile may only connect after the user signs in (our reading)</td>
<td>Machine-level Wi-Fi profile; see Step 9</td>
</tr>
<tr>
<td>VPN only</td>
<td>The tunnel comes up after logon, so startup never saw a DC (our reading)</td>
<td>The VPN section of the <a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy post</a></td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: DNS: can every DNS server this machine uses answer the locator? (read-only)</h2>
<p>Microsoft's DC locator article: "Clients query DNS for the appropriate SRV records and then ping those servers by using UDP-based LDAP pings." Its domain-not-found article names two DNS causes: "The client's Domain Name System (DNS) configuration isn't correct." and "The DNS server configuration on the domain controller isn't correct." Read both sides:</p>
<pre><code class="language-powershell"># Read-only. Microsoft's first look: the adapter's IP, suffix, and DNS server list
ipconfig /all

# Read-only. The SRV queries the locator makes (corp.contoso.com and site HQ are examples)
Resolve-DnsName -Name '_ldap._tcp.dc._msdcs.corp.contoso.com' -Type SRV -DnsOnly
Resolve-DnsName -Name '_ldap._tcp.HQ._sites.dc._msdcs.corp.contoso.com' -Type SRV -DnsOnly
Resolve-DnsName -Name 'DC01.corp.contoso.com' -Type A -DnsOnly

# Read-only. Ask each DNS server from ipconfig directly; one bad server in the list is enough (our habit)
foreach ($dns in '10.20.0.10', '10.20.0.11') {
  Resolve-DnsName -Name '_ldap._tcp.dc._msdcs.corp.contoso.com' -Type SRV -Server $dns -DnsOnly -ErrorAction SilentlyContinue |
    Select-Object @{ n = 'DnsServer'; e = { $dns } }, Name, NameTarget, Port
}
</code></pre>
<p>Microsoft's <code>nslookup</code> method for the same record expects SRV answers with <code>priority= 0 weight= 100 port= 389</code> and the DC's host name and address. The site-specific name format (<code>_ldap._tcp.SiteName._sites.dc._msdcs.DomainName</code>) comes from Microsoft's archived TechNet wiki on how DCs are located. The <code>-Server</code> loop is ours.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>A router, firewall, or public resolver in the DNS server list</td>
<td>It can't answer <code>_msdcs</code> queries for your domain. The accepted answer on a Microsoft Q&amp;A thread about 5719 says the same: DNS on the DC and the member should be the DC's address "and no others such as router or public DNS" (community answer)</td>
<td>Fix the DHCP option or the static setting (Step 9)</td>
</tr>
<tr>
<td>SRV query works on one DNS server, fails on another</td>
<td>Zone replication or a forwarder problem on that server</td>
<td>Fix that DNS server before any client change</td>
</tr>
<tr>
<td>No SRV answer from any server</td>
<td>The DCs' records are missing. Microsoft: "To force the domain controller service registration, stop and restart the Netlogon service." on the DC</td>
<td>Step 7</td>
</tr>
<tr>
<td>Domain SRV works, site SRV returns nothing</td>
<td>No DC registers in that site, or the site name is wrong</td>
<td>Step 5</td>
</tr>
<tr>
<td>Works now, failed at boot</td>
<td>A negative answer cached during the boot window. <code>ipconfig /displaydns</code> shows the cache; Microsoft says <code>/flushdns</code> can "discard negative cache entries from the cache"</td>
<td>Step 3 for the timing</td>
</tr>
</tbody></table>
<p>Microsoft's other DNS check is on the DC side: the <code>Netlogon.dns</code> file in <code>%systemroot%\System32\Config</code> lists the records a DC tries to register. "The first record in the file is the domain controller's Lightweight Directory Access Protocol (LDAP) SRV record."</p>
<hr />
<h2>Step 5: DC locator and the AD site (read-only)</h2>
<p>Microsoft's summary of the locator: "Locator finds which domain controllers are registered." and "An LDAP User Datagram Protocol (UDP) lookup is then sent to one or more of the domain controllers listed in the response to ensure their availability. Finally, the Netlogon service caches the discovered domain controller to aid in resolving future requests." That cache matters when you test. Microsoft's <code>nltest</code> reference for <code>/FORCE</code>: "Forces the computer to run the command against the DNS server instead of looking in the cache for the information."</p>
<pre><code class="language-console">:: Read-only. Cached answer first, then a forced rediscovery, then a KDC specifically
nltest /dsgetdc:corp.contoso.com
nltest /dsgetdc:corp.contoso.com /force
nltest /dsgetdc:corp.contoso.com /force /kdc

:: Read-only. The site Netlogon has for this machine, and the DCs it can list
nltest /dsgetsite
nltest /dclist:corp.contoso.com
</code></pre>
<p><code>/force /kdc</code> is the exact form in Microsoft's domain-not-found article. Microsoft's note on the output: "The list might not include all the available domain controllers." If you have the AD module, the same reads in PowerShell:</p>
<pre><code class="language-powershell"># Read-only. Forced discovery, the cached site name, and the AD subnets that map IPs to sites
Get-ADDomainController -Discover -DomainName corp.contoso.com -ForceDiscover | Select-Object Name, Site, IPv4Address
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -Name DynamicSiteName -ErrorAction SilentlyContinue
Get-ADReplicationSubnet -Filter * | Select-Object Name, Site, Location | Sort-Object Name
</code></pre>
<p><code>DynamicSiteName</code> as the place Windows stores its AD site comes from Microsoft's archived TechNet wiki. How the site gets picked, from the DC locator article: "the domain controller identifies which site the client is in based on the client's IP subnet." And: "If the domain controller isn't in the optimal site, the client flushes the cache after 15 minutes and discards the cache entry."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>/dsgetdc</code> works, <code>/dsgetdc /force</code> fails</td>
<td>The cache hides a break that's real right now</td>
<td>Steps 4, 6</td>
</tr>
<tr>
<td><code>/force</code> works now, 5719 at boot</td>
<td>Discovery is healthy once the network is up. The timing is the problem</td>
<td>Step 3</td>
</tr>
<tr>
<td><code>/dsgetsite</code> is empty, or the site is wrong</td>
<td>The client's subnet isn't in AD Sites and Services, or it maps to the wrong site</td>
<td>Step 9: add or fix the subnet</td>
</tr>
<tr>
<td><code>/dsgetdc</code> returns a DC across the WAN</td>
<td>No reachable DC in the client's site, or the site's DCs don't register</td>
<td>Steps 6, 7</td>
</tr>
<tr>
<td><code>nltest</code> fails with 1355</td>
<td>Microsoft lists error 1355 for a locator failure, with the message "The specified domain either doesn't exist or couldn't be contacted." (1355 is <code>ERROR_NO_SUCH_DOMAIN</code>, our note)</td>
<td>Steps 4, 6</td>
</tr>
<tr>
<td>A Windows Server 2025 member can't find DCs by a short (NetBIOS) domain name</td>
<td>Microsoft: "Beginning with Windows Server 2025, DC locator doesn't allow the use of NetBIOS-style location."</td>
<td>Use the DNS domain name; read Microsoft's <code>BlockNetBIOSDiscovery</code> section before you change it</td>
</tr>
</tbody></table>
<p>On that last row, Microsoft's guidance for the <code>BlockNetBIOSDiscovery</code> policy: "We recommend that you keep it set to TRUE. Disable it only for temporary periods while you're pursuing other mitigations."</p>
<hr />
<h2>Step 6: The ports between this machine and the DC (read-only)</h2>
<p>Microsoft's domain-not-found article starts with the firewalls: "Make sure that traffic is allowed on UDP port 389 (LDAP) and UDP port 53 (DNS)." Its Kerberos troubleshooting guidance lists the rest of the path (server port side):</p>
<table>
<thead>
<tr>
<th>Service</th>
<th>Server port (Microsoft)</th>
</tr>
</thead>
<tbody><tr>
<td>DNS</td>
<td>53 (TCP, UDP)</td>
</tr>
<tr>
<td>LDAP, including DC Locator</td>
<td>389 (TCP, UDP)</td>
</tr>
<tr>
<td>Kerberos</td>
<td>88 (TCP, UDP)</td>
</tr>
<tr>
<td>RPC endpoint mapper</td>
<td>135 (TCP)</td>
</tr>
<tr>
<td>RPC for LSA, SAM, NetLogon</td>
<td>49152-65535 (TCP)</td>
</tr>
<tr>
<td>SMB</td>
<td>445 (TCP)</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only. TCP reachability to one DC (DC01 is an example)
foreach ($port in 53, 88, 135, 389, 445) {
  Test-NetConnection -ComputerName DC01.corp.contoso.com -Port $port -WarningAction SilentlyContinue |
    Select-Object ComputerName, RemotePort, TcpTestSucceeded
}
</code></pre>
<p>Microsoft's <code>Test-NetConnection</code> page describes <code>-Port</code> as "the TCP port number on the remote computer," so this loop can't prove UDP 389 or UDP 53. A successful <code>nltest /dsgetdc /force</code> from Step 5 is the practical proof that the UDP LDAP ping got through, because that's how the locator checks a DC (our reading). Microsoft's own DC location article uses the PortQryUI tool for the UDP side.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>TCP 389 and 88 open, <code>/dsgetdc /force</code> fails</td>
<td>UDP 389 or UDP 53 blocked on the path</td>
<td>Firewall team, with Microsoft's port list</td>
</tr>
<tr>
<td>135 open, the dynamic range blocked</td>
<td>Discovery can work while RPC to Netlogon fails</td>
<td>The RPC range from the table</td>
</tr>
<tr>
<td>Fails only right after boot</td>
<td>A NAC or IPsec policy that opens the path late. Microsoft's Method 1: adjust the firewall settings or IPsec policies "that are changed to allow DC connectivity"</td>
<td>Step 9</td>
</tr>
<tr>
<td>Fails to one DC, works to another</td>
<td>That DC, or a firewall in front of it</td>
<td>Step 7</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: The DC side (read-only)</h2>
<p>When a whole site logs 5719 at once, look at the DCs before the clients. Microsoft's Netlogon service article lists what Netlogon does on a DC, including "Domain Name System (DNS) registration of NTDS records (A, AAAA, and SRV)." If Netlogon on the DC isn't running, or is paused, clients can't use it.</p>
<pre><code class="language-console">:: Read-only. Netlogon state on each DC (run from an admin workstation; DC01/DC02 are examples)
sc.exe \\DC01 query netlogon
sc.exe \\DC02 query netlogon

:: Read-only. On a DC: does it advertise, and can it register its locator records?
dcdiag /test:Advertising
dcdiag /test:RegisterInDNS /DnsDomain:corp.contoso.com
</code></pre>
<p>Microsoft's <code>dcdiag</code> reference: the Advertising test "fails if the Netlogon Service has stopped or failed to start," and RegisterInDNS "Tests whether the directory server can register the directory Server Locator DNS records." The <code>sc.exe \\DC01</code> form is ours.</p>
<p>A paused Netlogon is easy to miss. Microsoft: "When the Netlogon service is paused, the DC doesn't respond to DC Locator requests (on LDAP port UDP/389)." Its Netlogon log reasons:</p>
<table>
<thead>
<tr>
<th>Netlogon.log entry on the DC (Microsoft)</th>
<th>Microsoft's cause</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td><code>Netlogon Service Paused</code></td>
<td>"The Netlogon service is paused by an administrator."</td>
<td><code>net continue netlogon</code> after you find out why</td>
</tr>
<tr>
<td><code>NlInit: DS is paused</code></td>
<td>"The Directory service is paused."</td>
<td>The DC's directory service</td>
</tr>
<tr>
<td><code>Waiting for RPCSS</code></td>
<td>"RPC Subsystem startup pending."</td>
<td>The DC's RPC service</td>
</tr>
<tr>
<td><code>SysVol not ready</code></td>
<td>"The DFSR Initial replication not completed."</td>
<td>The <a href="https://dev.to/adminpackstudio/dfs-replication-not-working-a-read-first-backlog-event-4012-dirty-shutdown-and-sysvol-triage-a5i">DFS Replication post</a> (SYSVOL section)</td>
</tr>
</tbody></table>
<p><strong>When the DC itself logs 5719.</strong> Microsoft: "If this computer is a DC for the specified domain, it sets up a secure session with the primary domain controller (PDC) emulator in the specified domain. Otherwise, this computer sets up a secure session with any DC in the specified domain." So on a DC, read whether it can reach the PDC emulator (<code>nltest /dsgetdc:corp.contoso.com /pdc /force</code>) and whether its own DNS client settings can resolve the domain. One suspect for a 5719 at DC boot that clears by itself is a DC whose only DNS servers aren't answering yet when it starts (our reading).</p>
<hr />
<h2>Step 8: The Netlogon service on this machine, and the 24H2 "internal error" (read-only)</h2>
<p><strong>Netlogon didn't start.</strong> Microsoft's Netlogon startup article: "The Netlogon service depends on the Workstation service." When a dependency fails, the System log shows Service Control Manager errors like "The Netlogon service depends on the LanmanWorkstation service which failed to start because of the following error: The dependency service or group failed to start."</p>
<pre><code class="language-console">:: Read-only. Netlogon's configuration and dependencies (Microsoft's sc.exe example)
sc qc netlogon
sc query netlogon
sc qc lanmanworkstation
</code></pre>
<p>Other symptoms Microsoft lists when Netlogon isn't running: Time-Service event 159 ("W32time is unable to communicate with Netlogon Service."), <code>nltest /sc_query</code> failing with <code>1722 0x6ba RPC_S_SERVER_UNAVAILABLE</code>, and "System error 1792 has occurred. An attempt was made to logon, but the network logon service was not started." Microsoft's resolutions run from fixing a disabled dependency to SFC for a missing DLL, so read which service failed first.</p>
<p><strong>"An internal error occurred" on Windows 11 24H2.</strong> A long Microsoft Q&amp;A thread (community reports, not a Learn reference page) describes 5719 with that reason text on Windows 11 24H2 clients, starting after the upgrade and logged "even when I restart servrice NETLOGON" (sic), so it isn't boot timing. Several posters there tie it to machine passwords that stopped rotating, and one cites the April 2025 cumulative update. Microsoft's own pages confirm the update side. The KB5055523 release notes for Windows 11 24H2: "This update addresses an issue affecting machine password rotation in the Identity Update Manager certificate/ Public Key Cryptography for Initial Authentication (PKNIT) path." And Microsoft Learn: "Starting with the April Windows security update (KB5055523), Credential Guard protected machine accounts is temporarily disabled in Windows Server 2025 and Windows 11, version 24H2."</p>
<pre><code class="language-powershell"># Read-only. OS build, and the secure channel test (members only; no -Repair)
Get-CimInstance -ClassName Win32_OperatingSystem | Select-Object Caption, Version, BuildNumber
Test-ComputerSecureChannel -Verbose
</code></pre>
<p>Microsoft on <code>Test-ComputerSecureChannel</code>: it "returns $true if the channel is working correctly and $false if it is not," and "When you run it on Domain Controllers, it returns false positive errors." Without <code>-Repair</code> it only tests. The Q&amp;A posters report the channel testing <code>True</code> while 5719 kept appearing, so a <code>True</code> here doesn't close the ticket (community reports).</p>
<table>
<thead>
<tr>
<th>24H2 finding</th>
<th>What to do (our reading unless noted)</th>
</tr>
</thead>
<tbody><tr>
<td>Build older than the April 2025 update</td>
<td>Update first. Microsoft's note above is the documented fix for the password rotation path</td>
</tr>
<tr>
<td>Channel <code>True</code>, machine password not rotating</td>
<td>Read the password age in AD (the <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">trust relationship post</a> has the reads)</td>
</tr>
<tr>
<td>Someone set Machine Identity Isolation by GPO</td>
<td>Read Microsoft's page before you change it. Microsoft: if it "was previously set to Enabled in enforcement mode, the device must be unjoined and rejoined to the domain, as it's unable to authenticate otherwise."</td>
</tr>
<tr>
<td>Channel <code>False</code></td>
<td>The secure channel side, in the <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">trust relationship post</a></td>
</tr>
</tbody></table>
<p>One more read that isn't a read: Microsoft's <code>nltest</code> reference for <code>/sc_verify</code> says that if the secure channel doesn't work, "this parameter removes the existing channel, and then builds a new one." Use <code>nltest /sc_query:corp.contoso.com</code> to look. Microsoft: it "Reports on the state of the secure channel the last time that you used it."</p>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Rejoining the domain because of 5719</strong></td>
<td>Changes the computer account and doesn't touch the adapter, DNS, or the switch port. The next boot logs the same event</td>
<td>Steps 2-6: find the layer first</td>
</tr>
<tr>
<td><strong><code>Test-ComputerSecureChannel -Repair</code> on every PC that logs 5719</strong></td>
<td>A reset for a channel that may be fine. The boot race happens before any DC is reachable, so there's nothing to repair yet (our reading)</td>
<td>Test without <code>-Repair</code> (Step 8), and only reset when the <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">trust relationship post</a>'s checks say so</td>
</tr>
<tr>
<td><strong><code>nltest /sc_verify</code> as a "check"</strong></td>
<td>Microsoft: if the channel isn't working, it "removes the existing channel, and then builds a new one"</td>
<td><code>nltest /sc_query</code></td>
</tr>
<tr>
<td><strong>"Always wait for the network" for the whole domain</strong></td>
<td>Microsoft: "On servers running Windows Server 2008 or later, this policy setting is ignored during Group Policy processing at computer startup." On clients it makes sign-in wait, and it doesn't make Netlogon find a DC sooner (our reading)</td>
<td>Step 3: fix the timing; use the setting only where foreground processing is the point</td>
</tr>
<tr>
<td><strong>Disabling IPv6 with a <code>0xfffffff</code> value</strong></td>
<td>Microsoft: "We do not recommend that you disable IPv6 or its components." A wrong <code>DisabledComponents</code> value adds a five-second startup delay</td>
<td>Step 9: correct the value to <code>0xff</code> if someone set it</td>
</tr>
<tr>
<td><strong>Adding a public DNS server as "backup"</strong></td>
<td>It can't answer <code>_msdcs</code> queries for your domain, so the client sometimes gets no DC at all (our reading)</td>
<td>Step 4: only DNS servers that host or forward the AD zones</td>
</tr>
<tr>
<td><strong><code>ExpectedDialupDelay</code> at 600 everywhere</strong></td>
<td>Microsoft's own note: "This is only effective if the machine already has an IP address." It fits a quarantine network, not a slow DHCP or link</td>
<td>Step 3: find the delay first</td>
</tr>
<tr>
<td><strong>Ignoring every 5719 because Microsoft calls it safe to ignore</strong></td>
<td>Microsoft's line is conditional: safe "If you can log on to the domain without a problem." Missed startup GPOs and scripts count as a problem (our reading)</td>
<td>Step 1: list what else failed</td>
</tr>
<tr>
<td><strong>Flipping <code>MachineIdentityIsolation</code> from a forum post</strong></td>
<td>Microsoft: after enforcement mode, the device "must be unjoined and rejoined to the domain, as it's unable to authenticate otherwise."</td>
<td>Step 8: update first, then read Microsoft's page</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Change one layer, then watch (only after the read)</h2>
<p>Pick the one change Steps 1-8 point at. Do the network fixes first, because Microsoft lists them first. Save the keys before any registry change:</p>
<pre><code class="language-powershell"># Low-risk write. Save the Netlogon, TCP/IP, and Winlogon keys before you change them (our habit)
reg export HKLM\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters C:\Temp\netlogon-params-before.reg /y
reg export HKLM\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters C:\Temp\tcpip-params-before.reg /y
reg export "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" C:\Temp\winlogon-before.reg /y
</code></pre>
<p><strong>The boot race (Step 3), network side.</strong> Microsoft's Resolution 1: the current driver for the network adapter, or PortFast on the switch port. Both are owned outside Windows: the driver by whoever packages drivers, the switch port by the network team. For 802.1X or NAC, Microsoft's Method 1 is to adjust the firewall settings or IPsec policies so DC traffic is allowed as soon as the client has its address.</p>
<p><strong>The boot race, Group Policy side.</strong> Microsoft's Method 7: set "Specify startup policy processing wait time" (Computer Configuration\Administrative Templates\System\Group Policy). Microsoft: "The time it takes Netlogon to acquire a working IP can be the basis for the setting." Measure it from the Netlogon log in Step 2 (the gap between the first failure and the <code>Winsock Addrs</code> line), add a margin, and set that. The policy's own text: "If you disable or don't configure this policy setting, Group Policy will use the default wait time of 30 seconds on computers running Windows Vista operating system." Configuring it also works around the miscalculated-wait issue from Step 3. Microsoft's Method 9 is a registry value with the same goal, under <code>Winlogon</code>, set to 60 (decimal). Pick the GPO or the registry value, not both (our preference: the GPO):</p>
<pre><code class="language-powershell"># Change, on one test PC first. Microsoft's Method 9 value; restart after. Dry-run first
Set-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon' -Name GpNetworkStartTimeoutPolicyValue -Value 60 -Type DWord -WhatIf
</code></pre>
<p>Microsoft: "If the Group Policy startup script doesn't run, increase the value of the GpNetworkStartTimeoutPolicyValue registry entry."</p>
<p><strong>A NAC quarantine network that releases the PC late (Step 3).</strong> Microsoft's Method 2, <code>ExpectedDialupDelay</code> in seconds, "Data Range is between 0 and 600 seconds (10 minutes)," and only when the PC already has an address:</p>
<pre><code class="language-powershell"># Change. Example: 60 seconds, from your measured delay. Dry-run first
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -Name ExpectedDialupDelay -Value 60 -Type DWord -WhatIf
</code></pre>
<p><strong>Netlogon gives up on DCs too long after one failure.</strong> Microsoft's Method 4, <code>NegativeCachePeriod</code>: with it reduced, "the Netlogon service doesn't behave as if the domain controllers are offline for 45 seconds." Microsoft's suggestion: "Try to set a low value, such as three seconds. In LAN environments, you can use a value of 0 to turn off the negative cache." And: "The event 5719 is still logged." So this shortens the outage, not the event (our reading).</p>
<pre><code class="language-powershell"># Change. Microsoft's suggested low value. Dry-run first
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters' -Name NegativeCachePeriod -Value 3 -Type DWord -WhatIf
</code></pre>
<p><strong>Slow address uniqueness checks.</strong> Microsoft's Method 3: <code>ArpRetryCount</code> = 1 under <code>Tcpip\Parameters</code> ("The Data Range is between 0 and 3 (3 is default)."):</p>
<pre><code class="language-powershell"># Change. Microsoft's Method 3 value. Dry-run first
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters' -Name ArpRetryCount -Value 1 -Type DWord -WhatIf
</code></pre>
<p><strong>A wrong <code>DisabledComponents</code> value.</strong> Microsoft's Method 8: delete it, or change it to <code>0xff</code>. The path is ours, as in Step 3:</p>
<pre><code class="language-powershell"># Change. Only if Step 3 found 0xfffffff. Dry-run first
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters' -Name DisabledComponents -Value 0xff -Type DWord -WhatIf
</code></pre>
<p>Microsoft's KB lists more methods: a Kerberos <code>MaxPacketSize</code> value that "applies only to Windows XP and Windows Server 2003 or earlier versions," and <code>DisableDHCPMediaSense</code>. We'd skip the first on anything current and keep media sense as a last resort (our view).</p>
<p><strong>DNS (Step 4).</strong> Fix the DNS server list where it's set: the DHCP scope option, or the static adapter setting. Then, on the client, <code>ipconfig /flushdns</code> and a reboot test. If a DC's records are missing, Microsoft's steps on that DC: <code>ipconfig /registerdns</code> for its host record, and restart Netlogon to re-register the locator records:</p>
<pre><code class="language-powershell"># Change, on the DC with missing records (DC01). Clients on that DC fail over briefly. Dry-run first
Restart-Service -Name Netlogon -WhatIf
</code></pre>
<p><strong>A missing or wrong AD subnet (Step 5).</strong> Add the subnet to the right site. The subnet and site below are examples:</p>
<pre><code class="language-powershell"># Change. Map the client subnet to its site. Dry-run first
New-ADReplicationSubnet -Name '10.20.30.0/24' -Site HQ -Location 'HQ, 3rd floor' -WhatIf
</code></pre>
<p>The client picks up the new site on a later locator call. Microsoft's 15-minute cache rule from Step 5 applies to a DC in the wrong site.</p>
<p><strong>A paused or stopped Netlogon on a DC (Step 7).</strong> Find the cause from the Netlogon log reasons first. For <code>SysVol not ready</code>, fix SYSVOL replication; don't force the service.</p>
<p><strong>The 24H2 "internal error" (Step 8).</strong> Install the current cumulative update. Leave Machine Identity Isolation alone until you've read Microsoft's mode descriptions, because backing out of enforcement mode means a rejoin.</p>
<p><strong>Then prove it with a boot.</strong> Restart the PC and read only this boot's events:</p>
<pre><code class="language-powershell"># Read-only. 5719 and 1129 since this boot, a forced locator call, and the channel test
$boot = (Get-CimInstance -ClassName Win32_OperatingSystem).LastBootUpTime
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'NETLOGON'; Id = 5719; StartTime = $boot } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Message
Get-WinEvent -FilterHashtable @{ LogName = 'System'; ProviderName = 'Microsoft-Windows-GroupPolicy'; Id = 1129; StartTime = $boot } -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Message
nltest /dsgetdc:corp.contoso.com /force
Test-ComputerSecureChannel
</code></pre>
<p>Watch five or more boots, not one, because Microsoft notes the delay can alternate "between startups." Check that the startup script ran. Then turn the diagnostics back off: <code>Nltest /DBFlag:0x0</code>, and remove <code>GPSvcDebugLevel</code> if you set it.</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Boot race on wired PCs, same switch model</td>
<td>Current NIC driver or PortFast on access ports</td>
<td>Rejoining PCs</td>
</tr>
<tr>
<td>802.1X or NAC releases the port late</td>
<td>NAC/firewall timing, then <code>ExpectedDialupDelay</code> for the quarantine case</td>
<td><code>ExpectedDialupDelay</code> 600 on every PC</td>
</tr>
<tr>
<td>Startup GPO or script misses the window</td>
<td>"Specify startup policy processing wait time" from a measured delay</td>
<td>"Always wait for the network" for the whole domain</td>
</tr>
<tr>
<td>A public or router DNS server in the list</td>
<td>AD DNS servers only, via DHCP or the static setting</td>
<td>Hosts-file entries for DCs</td>
</tr>
<tr>
<td>No site, or the wrong site</td>
<td><code>New-ADReplicationSubnet</code> for the client subnet</td>
<td>Hard-coding a DC on the client</td>
</tr>
<tr>
<td>DC records missing</td>
<td><code>ipconfig /registerdns</code> and a Netlogon restart on that DC</td>
<td>Hand-made SRV records</td>
</tr>
<tr>
<td>Windows 11 24H2 "internal error"</td>
<td>Current cumulative update, then read the password age</td>
<td>Registry flips from forum threads</td>
</tr>
<tr>
<td>DC found, channel broken</td>
<td>The <a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">trust relationship post</a>'s repair path</td>
<td>Rejoin as the first move</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>What does Netlogon event ID 5719 mean?</strong>
The computer couldn't set up a secure session with a domain controller when Netlogon tried. The reason text after "due to the following:" says why. "There are currently no logon servers available to service the logon request" means no DC was found or answered at that moment. Microsoft's main causes are a network that wasn't ready yet, DNS that can't answer the locator's SRV queries, and blocked ports.</p>
<p><strong>Can I ignore event ID 5719?</strong>
Sometimes. Microsoft: "If you can log on to the domain without a problem, you can safely ignore event ID 5719." Check what else failed at the same time. A 1129 from Group Policy, a startup script that didn't run, or users who can't reach shares is a reason to work through the chain.</p>
<p><strong>Why does 5719 only happen at startup?</strong>
Usually because Netlogon looked for a DC before the network was ready: link negotiation, an 802.1X or NAC check, or a slow DHCP answer. Microsoft's first fixes are a current NIC driver and PortFast on the switch, then Group Policy's startup wait time. The Netlogon debug log shows the gap between the first failure and the address arriving.</p>
<p><strong>Why does Windows 11 24H2 log 5719 "An internal error occurred"?</strong>
Microsoft Q&amp;A threads report it on Windows 11 24H2 after the upgrade, even when Netlogon restarts, so it isn't boot timing. Microsoft's April 2025 update (KB5055523) fixed a machine password rotation issue and temporarily disabled Credential Guard protected machine accounts. Update first, then check whether the machine password is rotating.</p>
<p><strong>What does 5719 mean on a domain controller?</strong>
Microsoft: a DC "sets up a secure session with the primary domain controller (PDC) emulator in the specified domain." So check that the DC can reach the PDC emulator (<code>nltest /dsgetdc:yourdomain /pdc /force</code>) and that its own DNS servers answer at boot. On a DC, <code>Test-ComputerSecureChannel</code> "returns false positive errors," so use <code>nltest</code> there.</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/trust-relationship-between-this-workstation-and-the-primary-domain-failed-a-read-first-secure-ljo">Trust Relationship Between This Workstation and the Primary Domain Failed</a> (the secure channel and machine password repair side, once the machine can find a DC)</li>
<li><a href="https://dev.to/adminpackstudio/group-policy-not-applying-a-read-first-scope-filtering-replication-and-client-triage-31ho">Group Policy Not Applying</a> (event 1129, startup processing, and VPN users whose startup run never saw a DC)</li>
<li><a href="https://dev.to/adminpackstudio/windows-time-not-syncing-with-domain-controller-a-read-first-w32tm-pdc-emulator-and-vm-time-sync-536d">Windows Time Not Syncing With Domain Controller</a> (Time-Service events that show up when a machine can't find a DC, and the Kerberos five-minute window)</li>
</ul>
<h2>Free checklist</h2>
<p>A startup policy that never ran because the network wasn't ready is a change that didn't land where you thought. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=netlogon-event-id-5719">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=netlogon-event-id-5719</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=netlogon-event-id-5719">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=netlogon-event-id-5719</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 favor read-only checks: <code>Get-WinEvent</code>, <code>Get-CimInstance</code>, <code>Select-String</code> on the Netlogon log, <code>Get-NetAdapter</code>, <code>Get-NetIPConfiguration</code>, <code>Get-NetConnectionProfile</code>, <code>Get-ItemProperty</code>, <code>ipconfig /all</code> / <code>/displaydns</code>, <code>Resolve-DnsName</code>, <code>nltest /dsgetdc</code> / <code>/dsgetsite</code> / <code>/dclist</code> / <code>/sc_query</code>, <code>Get-ADDomainController -Discover</code>, <code>Get-ADReplicationSubnet</code>, <code>Test-NetConnection</code>, <code>sc qc</code> / <code>sc query</code>, <code>dcdiag /test:Advertising</code> / <code>RegisterInDNS</code>, and <code>Test-ComputerSecureChannel</code> without <code>-Repair</code>. <code>Nltest /DBFlag</code> and <code>GPSvcDebugLevel</code> are diagnostic changes; <code>reg export</code> writes local files. Driver and switch changes, the Group Policy wait time, <code>GpNetworkStartTimeoutPolicyValue</code>, <code>ExpectedDialupDelay</code>, <code>NegativeCachePeriod</code>, <code>ArpRetryCount</code>, <code>DisabledComponents</code>, DNS server changes, <code>ipconfig /flushdns</code> / <code>/registerdns</code>, restarting Netlogon on a DC, new AD subnets, <code>nltest /sc_verify</code>, and secure channel repairs are deliberate changes. Change one layer at a time, test on one machine first, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item><item><title><![CDATA[Windows Server Backup Failed: A Read-First Event ID, VSS Writer, Shadow Storage, and EFI Lock Triage]]></title><description><![CDATA[Windows Server Backup failed? Read the event ID, target, shadow storage, VSS writers, and EFI lock before you delete shadows or the catalog.]]></description><link>https://adminpackstudio.hashnode.dev/windows-server-backup-failed-a-read-first-event-id-vss-writer-shadow-storage-and-efi-lock-triage</link><guid isPermaLink="true">https://adminpackstudio.hashnode.dev/windows-server-backup-failed-a-read-first-event-id-vss-writer-shadow-storage-and-efi-lock-triage</guid><category><![CDATA[windows server]]></category><category><![CDATA[Backup]]></category><category><![CDATA[sysadmin]]></category><category><![CDATA[Powershell]]></category><dc:creator><![CDATA[AdminPackStudio]]></dc:creator><pubDate>Sun, 18 Oct 2026 04:39:14 GMT</pubDate><content:encoded><![CDATA[<p>Helpdesk: "FS01's nightly Windows Server Backup failed again. Last night it said 'There is not enough disk space to create the volume shadow copy on the storage location,' the night before it was 'Windows Backup failed to get an exclusive lock on the EFI system partition (ESP),' and the backup disk still has 1.2 TB free. A forum thread says to delete every shadow copy, run <code>wbadmin delete catalog</code>, and reinstall the Windows Server Backup feature. Can we just do that tonight?"</p>
<p>Don't run any of that yet. <strong>"Windows Server Backup failed" is a wrapper, and the line underneath it tells you which layer actually broke.</strong> Microsoft's own troubleshooting article for Windows Server Backup says it plainly: "Windows Server Backup relies on multiple layers in the operating system to function. It can show errors when any of those layers report an error." Deleting shadow copies, wiping the catalog, and reinstalling the feature all hit the wrong layer for most of those errors, and two of them throw away things you may not get back. Work through it as a chain: <strong>the exact message, the hex code, the event ID, and which job failed</strong> → <strong>the job history and the Backup events (<code>wbadmin get versions</code>, <code>Get-WBJob</code>, the Microsoft-Windows-Backup log)</strong> → <strong>the backup target: is it there, writable, and big enough?</strong> → <strong>the source volumes and their shadow copy storage, including the small hidden partitions</strong> → <strong>the VSS writers and the services behind them</strong> → <strong>access denied and the EFI system partition lock</strong> → <strong>network share targets</strong> → <strong>the backup engine, the schedule, and the catalog</strong>. <code>wbadmin</code>, <code>Get-WBSummary</code>, <code>Get-WBJob</code>, <code>Get-WBPolicy</code>, <code>Get-WinEvent</code>, <code>vssadmin list writers</code>, <code>vssadmin list shadowstorage</code>, <code>Get-Volume</code>, and <code>Get-Partition</code> are the sources for that chain. Reinstall the feature and the next run fails the same way, only now without the old backup history.</p>
<p>If the failing item is a Hyper-V virtual machine and the error comes from the Hyper-V VSS writer or a checkpoint, read <a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed</a> after this one; it covers guest VSS, the integration service, and restarting VSS and its provider in depth, so this post doesn't repeat that. If a SQL Server on the same box is involved, <a href="https://dev.to/adminpackstudio/sql-server-transaction-log-full-error-9002-a-read-first-logreusewaitdesc-log-backup-and-1i91">SQL Server Transaction Log Full (Error 9002)</a> covers the log-backup side. This post covers <strong>"a Windows Server Backup job (scheduled, one-time, system state, or bare metal) fails with 'failed to create shadow copy,' 'not enough space,' 'failed to get an exclusive lock on the EFI system partition,' 'access is denied,' 'the device is not ready,' or 'there are no more files,' and you need a read-first path through the events, the target, shadow copy storage, the VSS writers, permissions, network shares, and the backup engine before you delete shadow copies, delete the catalog, or reinstall anything."</strong></p>
<h2>TL;DR</h2>
<ul>
<li><strong>The first error isn't always the cause.</strong> Microsoft's method: check the Backup events, then "Identify which application or layer caused the issue," then read that layer's own events, and repeat until you reach the root cause.</li>
<li><strong>"Not enough space" usually means a source volume, not the backup disk.</strong> Microsoft's minimums for shadow copy creation: "For volumes less than 500 megabytes, the minimum is 50 megabytes of free space. For volumes more than 500 megabytes, the minimum is 320 megabytes of free space." Small hidden partitions are the usual suspects (our reading).</li>
<li><strong>One failed writer fails the whole job.</strong> Microsoft: "If any of the VSS writers encounter an error, the entire backup job will fail." Run <code>vssadmin list writers</code> right after a failure.</li>
<li><strong>An EFI lock is usually another program on the ESP.</strong> Microsoft's Azure Backup page: the EFI system partition lock "can be due to system partition access by a third-party security or back up software."</li>
<li><strong>Network shares keep one copy.</strong> Microsoft: back up the same computer to the same remote shared folder again and "you will overwrite your previous backup."</li>
<li><strong>Deleting is permanent.</strong> Microsoft: "Deleting a shadow copy is permanent." And after <code>wbadmin delete catalog</code>, you can no longer reach that computer's backups from the snap-in.</li>
</ul>
<hr />
<h2>Where in the chain did it break?</h2>
<table>
<thead>
<tr>
<th>Step</th>
<th>What should be true</th>
<th>Typical symptom when it breaks</th>
</tr>
</thead>
<tbody><tr>
<td>1. Symptom captured</td>
<td>Exact message, hex code, event ID, job type, and target written down</td>
<td>"Backup failed" with nothing else</td>
</tr>
<tr>
<td>2. Job history and events</td>
<td>Last success known; the failing layer named in the events</td>
<td>Event 517 or 521 with a code nobody looked up</td>
</tr>
<tr>
<td>3. Backup target</td>
<td>Target online, writable, and big enough</td>
<td>"No target could be found"; "the device is not ready"; space on the target</td>
</tr>
<tr>
<td>4. Source volumes and shadow storage</td>
<td>Every volume in the backup has room for its shadow copy</td>
<td>"Not enough disk space to create the volume shadow copy" with a mostly empty backup disk</td>
</tr>
<tr>
<td>5. VSS writers</td>
<td>Every writer stable; VSS services can start</td>
<td>"Failed to create shadow copy"; <code>0x807800A1</code>; a writer in <code>[9] Failed</code></td>
</tr>
<tr>
<td>6. Access and the ESP</td>
<td>Elevated, right group, SYSTEM on System Volume Information, ESP not held</td>
<td>"Access is denied"; "failed to get an exclusive lock on the EFI system partition"</td>
</tr>
<tr>
<td>7. Network share target</td>
<td>Share reachable, writable, not overwritten mid-job</td>
<td><code>0x80070012</code> "There are no more files"; network target not writeable</td>
</tr>
<tr>
<td>8. Engine, schedule, catalog</td>
<td>Engine service starts; schedule exists; catalog readable</td>
<td>Engine could not be contacted; schedule not found; WSB won't start after a restore</td>
</tr>
<tr>
<td>9. Change</td>
<td>Only after Steps 1-8 agree: one layer, one change</td>
<td>Shadow copies gone, catalog gone, still failing</td>
</tr>
</tbody></table>
<hr />
<h2>Step 1: Capture the symptom (read-only)</h2>
<p>Write these on the ticket first:</p>
<table>
<thead>
<tr>
<th>Fact</th>
<th>Why it matters</th>
</tr>
</thead>
<tbody><tr>
<td><strong>The exact message, word for word</strong></td>
<td>"Not enough space" and "access is denied" each have several different sources (Steps 3, 4, 6)</td>
</tr>
<tr>
<td><strong>The hex or decimal error code</strong></td>
<td>Microsoft's tables map codes like <code>0x807800A1</code> to a layer (Step 2)</td>
</tr>
<tr>
<td><strong>The event ID and log</strong></td>
<td>517, 521, 5, 49, 50, and 564 each point at a different step</td>
</tr>
<tr>
<td><strong>Which job</strong></td>
<td>Scheduled, one-time, system state, or bare metal (<code>-allCritical</code>). Each touches different volumes</td>
</tr>
<tr>
<td><strong>The target type</strong></td>
<td>Dedicated disk, volume, or remote shared folder. Network targets have their own rules (Step 7)</td>
</tr>
<tr>
<td><strong>Every night, or now and then?</strong></td>
<td>Every run reads as a configuration or space problem. Random runs read as a lock, a timeout, or contention (our reading)</td>
</tr>
<tr>
<td><strong>What changed?</strong></td>
<td>New AV or EDR agent, new app with a VSS writer, disk swap, BIOS-to-UEFI move, a restore, a new share</td>
</tr>
</tbody></table>
<p>Then branch on what you see:</p>
<table>
<thead>
<tr>
<th>What you see</th>
<th>What it usually means (our reading unless noted)</th>
<th>Start at</th>
</tr>
</thead>
<tbody><tr>
<td>"A Volume Shadow Copy Service operation failed" or <code>0x807800A1</code></td>
<td>VSS or a writer. Microsoft's message says to check the "VSS" and "SPP" application logs</td>
<td>Steps 2, 5</td>
</tr>
<tr>
<td>"There is not enough disk space to create the volume shadow copy"</td>
<td>A source volume is short on free space for its shadow copy. Microsoft says it "applies to both the backup storage destination and volumes included in the backup"</td>
<td>Step 4</td>
</tr>
<tr>
<td>"Backup failed as required space was not available on the backup target" (event 50)</td>
<td>The target itself is full</td>
<td>Step 3</td>
</tr>
<tr>
<td>"Failed to get an exclusive lock on the EFI system partition"</td>
<td>Something else is holding the ESP during the snapshot</td>
<td>Step 6</td>
</tr>
<tr>
<td>"Access is denied" with no ESP wording</td>
<td>Elevation, group membership, System Volume Information, a writer's own access, or the share</td>
<td>Steps 5, 6, 7</td>
</tr>
<tr>
<td>"The device is not ready" or "no target could be found"</td>
<td>The target disk went offline, slept, or was swapped</td>
<td>Step 3</td>
</tr>
<tr>
<td><code>0x80070012</code> "There are no more files" to a share</td>
<td>The share target and the SMB client cache</td>
<td>Step 7</td>
</tr>
<tr>
<td>"System writer is not found in the backup"</td>
<td>The System Writer itself, usually permissions (Microsoft KB)</td>
<td>Step 5</td>
</tr>
<tr>
<td>Engine could not be contacted; WSB won't start after a bare metal restore</td>
<td>The engine service or the catalog</td>
<td>Step 8</td>
</tr>
</tbody></table>
<hr />
<h2>Step 2: Read the job history and the Backup events (read-only)</h2>
<p>Start with what Windows Server Backup already knows. Microsoft: <code>Get-WBSummary</code> returns "the next scheduled backup, details of the last backup, and details of the last successful backup," and <code>Get-WBJob -Previous 10</code> "gets the status of the last 10 backup or recovery jobs from the backup events in the event manager."</p>
<pre><code class="language-powershell"># Read-only, elevated, on FS01. What ran, what failed, what's scheduled
Get-WBSummary
Get-WBJob -Previous 5
Get-WBPolicy

# Read-only. Backups the catalog knows about, and where they live
wbadmin get versions
</code></pre>
<p>One trap: Microsoft says <code>wbadmin get versions</code> with no parameters "lists all backups of the local computer, even if those backups are not available." A version in that list isn't proof the disk is attached or the files are readable (our reading). Add <code>-backupTarget:E:</code> to read the target itself.</p>
<p>If a job is running now, <code>wbadmin get status</code> shows it. Microsoft: "This command doesn't stop until the backup or recovery operation is finished." Press Ctrl+C to leave it; don't use <code>wbadmin stop job</code> to get your prompt back, because "Canceled operations can't be restarted."</p>
<p>Then the events. The Application log carries <code>Microsoft-Windows-Backup</code> events (Microsoft's KB samples show 517 and 521 there), and Microsoft's DPM article shows event 5 in a log named <code>Microsoft-Windows-Backup</code>:</p>
<pre><code class="language-powershell"># Read-only. Backup failures in the Application log, last 14 days
Get-WinEvent -FilterHashtable @{
  LogName      = 'Application'
  ProviderName = 'Microsoft-Windows-Backup'
  StartTime    = (Get-Date).AddDays(-14)
} -ErrorAction SilentlyContinue | Where-Object LevelDisplayName -eq 'Error' |
  Select-Object TimeCreated, Id, Message | Format-List

# Read-only. The Backup operational channel (log name from Microsoft's DPM sample)
Get-WinEvent -LogName 'Microsoft-Windows-Backup' -MaxEvents 30 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Id, LevelDisplayName, Message | Format-List

# Read-only. Turn a decimal code from the message into hex (our helper)
'{0:X}' -f 2155348129
</code></pre>
<p>That last line prints <code>807800A1</code>, the code in Microsoft's own table below (our arithmetic). Microsoft's error-list tables give codes in both forms, so either one works for the lookup.</p>
<p>Microsoft's Windows Server 2008 operations page lists the Backup events. It's old, but later Microsoft KBs and Microsoft Q&amp;A threads on Windows Server 2022 still show the same 517 and 521 IDs:</p>
<table>
<thead>
<tr>
<th>Event</th>
<th>Microsoft's message (trimmed)</th>
<th>Read it as</th>
</tr>
</thead>
<tbody><tr>
<td>4</td>
<td>"Backup finished successfully."</td>
<td>The good one. Your confirm in Step 9</td>
</tr>
<tr>
<td>5 / 517</td>
<td>"Backup started at '%1' failed with following error code '%2' (%3)."</td>
<td>Generic failure. Read the code and the text in brackets</td>
</tr>
<tr>
<td>9 / 521</td>
<td>"failed as Volume Shadow copy operation failed for backup volumes"</td>
<td>VSS. Go to Step 5</td>
</tr>
<tr>
<td>17 / 544</td>
<td>"failed because Windows Backup engine could not be contacted"</td>
<td>The engine service (Step 8)</td>
</tr>
<tr>
<td>18 / 545</td>
<td>"failed because the schedule settings was not found"</td>
<td>The schedule (Step 8)</td>
</tr>
<tr>
<td>20 / 518</td>
<td>"failed as another backup or recovery is in progress"</td>
<td>Overlap with another job or product</td>
</tr>
<tr>
<td>21 / 528</td>
<td>"Scheduled backup configuration conflicts with group policy settings"</td>
<td>A Backup policy setting (Step 8)</td>
</tr>
<tr>
<td>49 / 561</td>
<td>"Backup failed as no target could be found."</td>
<td>Target missing or offline (Step 3)</td>
</tr>
<tr>
<td>50</td>
<td>"Backup failed as required space was not available on the backup target."</td>
<td>Target full (Step 3)</td>
</tr>
<tr>
<td>52 / 564</td>
<td>"Backup task failed as the configured network target is not writeable."</td>
<td>Share permissions (Step 7)</td>
</tr>
</tbody></table>
<p>Microsoft's layered method, from the Windows Server Backup VSS troubleshooting article: read the Backup failure event, "Identify which application or layer caused the issue," check that application's own events, and "Repeat step 2 and step 3 until you get to the root cause of the problem and then correct the problem." Its sample walks from a Backup event to a VSS event to the Hyper-V VMMS log and finally to a "not enough disk space" VSS event inside one guest. The backup error on the host said none of that.</p>
<table>
<thead>
<tr>
<th>Code (Microsoft's tables)</th>
<th>Microsoft's message</th>
<th>Layer</th>
</tr>
</thead>
<tbody><tr>
<td><code>0x807800A1</code></td>
<td>"A Volume Shadow Copy Service operation failed. Please check "VSS" and "SPP" application event logs for more information."</td>
<td>VSS (Step 5)</td>
</tr>
<tr>
<td><code>0x8078008a</code></td>
<td>"The read operation from the volume shadow copy on one of the source volumes failed."</td>
<td>A source volume (Steps 4, 5)</td>
</tr>
<tr>
<td><code>0x800423F3</code></td>
<td>"The writer experienced a transient error. If the backup process is retried, the error may not reoccur."</td>
<td>A writer, retry first</td>
</tr>
<tr>
<td><code>0x800423F4</code></td>
<td>"The writer experienced a non-transient error. If the backup process is retried, the error is likely to reoccur."</td>
<td>A writer, find which one</td>
</tr>
<tr>
<td><code>0x80042319</code></td>
<td>"A writer did not respond to a GatherWriterStatus call."</td>
<td>A stuck writer</td>
</tr>
<tr>
<td><code>0x81000101</code></td>
<td>"The creation of a shadow copy has timed out. Try this operation again."</td>
<td>Timing or load (Step 5)</td>
</tr>
</tbody></table>
<p>The text log Windows Server Backup writes for system state jobs is also worth a look. Microsoft's KB shows <code>C:\Windows\Logs\WindowsServerBackup\SystemStateBackup_Error &lt;DateTime&gt;.log</code> as the list of files that failed.</p>
<hr />
<h2>Step 3: The backup target (read-only)</h2>
<pre><code class="language-powershell"># Read-only. Disks Windows Server Backup can see right now
wbadmin get disks
Get-WBDisk

# Read-only. Where the policy sends backups
$policy = Get-WBPolicy
Get-WBBackupTarget -Policy $policy

# Read-only. Free space on the target volume (E: here)
Get-Volume -DriveLetter E | Select-Object DriveLetter, FileSystemLabel, FileSystem, HealthStatus, SizeRemaining, Size

# Read-only. Backups actually stored on that target
wbadmin get versions -backupTarget:E:
</code></pre>
<p>Microsoft: <code>Get-WBDisk</code> "gets a list of internal and external disks that are online for the local computer." A scheduled target that isn't in that list explains events 49 and 561 (our reading).</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Target disk missing from <code>wbadmin get disks</code></td>
<td>Offline, unplugged, or swapped. "The device is not ready" usually lands here</td>
<td>Reconnect or replace it, then re-read</td>
</tr>
<tr>
<td>A USB target that fails only some nights</td>
<td>Power management or a flaky enclosure</td>
<td>Check the System log for disk events at backup time</td>
</tr>
<tr>
<td>Target present, event 50</td>
<td>The target really is full</td>
<td>Step 9: space or retention on the target</td>
</tr>
<tr>
<td><code>-backupTarget</code> points at a volume that's also in the backup</td>
<td>Microsoft: a critical-volume target "can't be any of the volumes that are included in the backup"</td>
<td>Pick a different target</td>
</tr>
<tr>
<td>A system state backup to the system volume</td>
<td>Microsoft's KB 944530 (Windows Server 2008 and 2008 R2) blocks it by default and needs "twice the amount of free space as the size of the system state backup" if you allow it</td>
<td>Use a separate volume</td>
</tr>
</tbody></table>
<hr />
<h2>Step 4: Source volumes and shadow copy storage (read-only)</h2>
<p>This is where most "not enough space" tickets end. The VSS message Microsoft quotes is "There is not enough disk space to create the volume shadow copy on the storage location," and Microsoft adds: "This applies to both the backup storage destination and volumes included in the backup." Its minimums:</p>
<table>
<thead>
<tr>
<th>Volume size</th>
<th>Microsoft's minimum free space for shadow copy creation</th>
</tr>
</thead>
<tbody><tr>
<td>Under 500 MB</td>
<td>50 MB</td>
</tr>
<tr>
<td>Over 500 MB</td>
<td>320 MB</td>
</tr>
<tr>
<td>Over 1 GB</td>
<td>"It is recommended that least 1 gigabyte of free disk space on each volume" (sic)</td>
</tr>
</tbody></table>
<p>A bare metal or all-critical backup includes the small partitions too: the EFI system partition, a recovery partition, or a System Reserved partition. Those are the volumes that run short of 50 or 320 MB, while C: and the backup disk look fine (our reading). Read them all, with sizes:</p>
<pre><code class="language-powershell"># Read-only. Every partition with its size and free space, including the hidden ones
Get-Partition | ForEach-Object {
  $v = $_ | Get-Volume -ErrorAction SilentlyContinue
  [pscustomobject]@{
    Disk      = $_.DiskNumber
    Partition = $_.PartitionNumber
    Type      = $_.Type
    Letter    = $_.DriveLetter
    SizeMB    = [math]::Round($_.Size / 1MB)
    FreeMB    = if ($v) { [math]::Round($v.SizeRemaining / 1MB) } else { $null }
    FS        = $v.FileSystem
  }
} | Format-Table -AutoSize

# Read-only. Which volumes the scheduled backup includes, and which ones Windows calls critical
Get-WBVolume -Policy (Get-WBPolicy)
Get-WBVolume -CriticalVolumes
</code></pre>
<p>The join and the selected properties are ours. Microsoft's <code>Get-WBVolume</code> page: it can show "volumes included in the WBPolicy object, all critical volumes, all volumes, or volumes in the WBDisk object."</p>
<p>Then the shadow copy storage itself. Microsoft: "A shadow copy storage association links a volume that has shadow copies to the volume that stores those copies, and reports the disk space that storage uses."</p>
<pre><code class="language-powershell"># Read-only. Where shadow copy storage lives, its used/allocated/maximum size, and existing copies
vssadmin list shadowstorage
vssadmin list shadows
vssadmin list volumes
</code></pre>
<p>What Microsoft's VSS overview says about that storage (the "diff area"):</p>
<table>
<thead>
<tr>
<th>Question</th>
<th>Microsoft's answer</th>
</tr>
</thead>
<tbody><tr>
<td>Where can it live?</td>
<td>"it must be located on an NTFS volume that has enough space to store it"</td>
</tr>
<tr>
<td>How is the location chosen?</td>
<td>An existing shadow copy's location first, then a manual association, then free space</td>
</tr>
<tr>
<td>What happens when it runs out?</td>
<td>"Shadow copies for the volume are deleted, beginning with the oldest shadow copy."</td>
</tr>
</tbody></table>
<p>And the system event Microsoft quotes when a backup can't grow that storage: "There was insufficient disk space on volume C: to grow the shadow copy storage for shadow copies of C: due to this failure all shadow copies of volume C: are at risk of being deleted."</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>A recovery or System Reserved partition under 50 MB free (or under 320 MB on a larger one)</td>
<td>Below Microsoft's minimum for that size</td>
<td>Step 9: free or grow that partition, or exclude it if it isn't critical</td>
</tr>
<tr>
<td>C: nearly full; the backup disk empty</td>
<td>The source, not the target</td>
<td>Step 9: free space on C:</td>
</tr>
<tr>
<td><code>list shadowstorage</code> shows a small maximum, nearly all used</td>
<td>The diff area is capped too low for the night's changes</td>
<td>Step 9: resize after a read</td>
</tr>
<tr>
<td>Many old shadow copies on a source volume</td>
<td>Previous Versions or another tool keeps its own copies. They share the diff area</td>
<td>Find the owner before you delete any</td>
</tr>
<tr>
<td>The failure is inside a Hyper-V guest</td>
<td>Microsoft's sample traces it to free space in the guest</td>
<td>Read the <a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V checkpoint post</a> Steps 3 and 5</td>
</tr>
</tbody></table>
<hr />
<h2>Step 5: VSS writers and the services behind them (read-only)</h2>
<p>Microsoft's VSS overview names four parts: the VSS service, the requester (here, Windows Server Backup), writers ("The component that guarantees you have a consistent data set to back up"), and providers. Then the timing that matters in a busy window: "The application freeze isn't allowed to take longer than 60 seconds," and "The shadow copy creation period lasts no more than 10 seconds." Past either limit, Microsoft says the shadow copy creation can be aborted.</p>
<p>Run these right after a failure, before anything restarts:</p>
<pre><code class="language-powershell"># Read-only, elevated. Writer state and last error, providers, and the services VSS needs
vssadmin list writers
vssadmin list providers
Get-Service -Name VSS, swprv, EventSystem, SENS, RpcSs, CryptSvc, wbengine |
  Select-Object Name, DisplayName, Status, StartType

# Read-only. VSS events around the failure, last 2 days (read SPP and the System log's VolSnap the same way)
Get-WinEvent -FilterHashtable @{
  LogName      = 'Application'
  ProviderName = 'VSS'
  StartTime    = (Get-Date).AddDays(-2)
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Id, LevelDisplayName, Message -First 20 | Format-List
</code></pre>
<p>Microsoft's Azure Backup system state page lists the services Windows Server Backup depends on:</p>
<table>
<thead>
<tr>
<th>Service</th>
<th>Startup type (Microsoft)</th>
</tr>
</thead>
<tbody><tr>
<td>Remote Procedure Call (RPC)</td>
<td>Automatic</td>
</tr>
<tr>
<td>COM+ Event System (EventSystem)</td>
<td>Automatic</td>
</tr>
<tr>
<td>System Event Notification Service (SENS)</td>
<td>Automatic</td>
</tr>
<tr>
<td>Volume Shadow Copy (VSS)</td>
<td>Manual</td>
</tr>
<tr>
<td>Microsoft Software Shadow Copy Provider (SWPRV)</td>
<td>Manual</td>
</tr>
</tbody></table>
<p><code>VSS</code> and <code>swprv</code> showing Stopped between jobs is normal for Manual services (our reading). Disabled is not.</p>
<p>Then read the writer list. Microsoft's own sample output shows the two states that matter, <code>State: [1] Stable</code> and <code>State: [9] Failed</code>, with a <code>Last error</code> line under each.</p>
<table>
<thead>
<tr>
<th>Writer finding</th>
<th>What Microsoft says</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>The SQL Server writer failed, SQLWRITER and SQLVDI errors in the Application log</td>
<td>KB 2615182: "Usually, the problematic SQL Server instance will be named in the first recorded SQLVDI error."</td>
<td>Find the instance and database first (Step 9)</td>
</tr>
<tr>
<td><code>System Writer</code> missing from the list; "System writer is not found in the backup"</td>
<td>KB 2009272: "The system writer fails because permissions to files in the %windir%\winsxs\filemaps\ or %windir%\winsxs\temp\PendingRenames directories are incorrect." Look for CAPI2 event 513</td>
<td>Step 9, after you confirm the event</td>
</tr>
<tr>
<td><code>System Writer</code> still missing; VSS event 8213 names NETWORK SERVICE</td>
<td>Same KB: the writer's host process "does not run under a user with sufficient access rights"</td>
<td>The <code>VssAccessControl</code> value in Step 9</td>
</tr>
<tr>
<td><code>Microsoft Hyper-V VSS Writer</code> failed</td>
<td>The VM layer. Microsoft's sample traces it into one guest</td>
<td>The <a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V checkpoint post</a></td>
</tr>
<tr>
<td>Writer error <code>0x800423f2</code>, "the writer's time-out expired between the Freeze and Thaw events."</td>
<td>Azure Backup page: "VSS writer is unable to complete in time due to lack of CPU and memory resources on the machine" or "Another backup software is already using the VSS writer"</td>
<td>Move the window or stop the overlap</td>
</tr>
<tr>
<td>Third-party writer failed</td>
<td>Microsoft's VSS FAQ, for non-Microsoft backup software that fails with a VSS error: check that company's support site for a product update. The same habit fits a vendor's writer (our reading)</td>
<td>That vendor's guidance</td>
</tr>
<tr>
<td>Every writer stable, backup still fails at the snapshot</td>
<td>Not a writer. Look at space (Step 4) and providers</td>
<td><code>vssadmin list providers</code> for a SAN or vendor provider</td>
</tr>
</tbody></table>
<p>One rule from Microsoft's SQL KB is worth keeping for any writer: "the first step would be to test the backup with that SQL Server instance stopped." In other words, prove the writer is the cause by taking it out of one test run (our generalization). For restarting VSS and <code>swprv</code> themselves, the Hyper-V post's Step 9 has the <code>-WhatIf</code> forms.</p>
<hr />
<h2>Step 6: Access denied and the EFI system partition lock (read-only)</h2>
<p>"Access is denied" has at least five different owners in Windows Server Backup:</p>
<table>
<thead>
<tr>
<th>Where the denial comes from</th>
<th>What Microsoft says</th>
<th>Check</th>
</tr>
</thead>
<tbody><tr>
<td>You, running <code>wbadmin</code></td>
<td>Members of Backup Operators or Administrators, and "you must run wbadmin from an elevated command prompt"</td>
<td><code>whoami /groups</code>; an elevated window</td>
</tr>
<tr>
<td>System Volume Information</td>
<td>"Ensure that the Local SYSTEM has full control on the System Volume Information folder located in the volume where Windows is installed." and "Windows Server backup can fail if the permissions above aren't set correctly."</td>
<td>Read the ACL (below)</td>
</tr>
<tr>
<td>The System Writer</td>
<td>CAPI2 event 513: "BeginFileEnumeration() failed. System Error: Access is denied."</td>
<td>Step 5</td>
</tr>
<tr>
<td>A network share</td>
<td>Event 564: "Please ensure user '%3' has write permissions on the target."</td>
<td>Step 7</td>
</tr>
<tr>
<td>The EFI system partition</td>
<td>"System state backup operation failed as the EFI system partition is locked."</td>
<td>This step</td>
</tr>
</tbody></table>
<pre><code class="language-powershell"># Read-only. Your groups, and the ACL on System Volume Information (display only)
whoami /groups | Select-String -Pattern 'Administrators|Backup Operators'
icacls 'C:\System Volume Information'

# Read-only. The EFI system partition: present, sized, and on which disk
Get-Partition | Where-Object Type -eq 'System' | Select-Object DiskNumber, PartitionNumber, Size, GptType

# Read-only. When the ESP lock failures happen, and what else ran then (517s with ESP text)
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'Microsoft-Windows-Backup'; Id = 517 } -MaxEvents 20 -ErrorAction SilentlyContinue |
  Where-Object { $_.Message -match 'EFI' } | Select-Object TimeCreated, Message
</code></pre>
<p>The <code>icacls</code> read and the <code>Type -eq 'System'</code> filter are ours. On a GPT disk the ESP is usually the partition Windows labels <code>System</code> (our reading).</p>
<p><strong>The ESP lock.</strong> Microsoft's Azure Backup troubleshooting page gives the cause: "This can be due to system partition access by a third-party security or back up software." Its fixes are to have the security vendor allow the backup, and if another backup product is running, "wait for it to finish and then retry backup." Microsoft Q&amp;A threads describe the same error as intermittent on Windows Server 2022, logged as event 517 (community threads, not a Learn reference page). One accepted answer says "a workaround from MS has been applied": a scheduled task that reruns the backup when event 517 appears.</p>
<table>
<thead>
<tr>
<th>ESP finding</th>
<th>What it means (our reading unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Fails every night</td>
<td>Something holds the ESP on a schedule: an EDR scan, another backup agent, a disk tool</td>
<td>Find it, then exclude or reschedule (Step 9)</td>
</tr>
<tr>
<td>Fails once or twice a month, retry works</td>
<td>The intermittent pattern in the Q&amp;A threads</td>
<td>A retry task is a workaround, not a fix. Keep watching</td>
</tr>
</tbody></table>
<hr />
<h2>Step 7: Network share targets (read-only)</h2>
<p>Windows Server Backup to a share has rules a local disk doesn't:</p>
<ul>
<li><strong>One copy.</strong> Microsoft: "If you save your backup to a remote shared folder, and then perform another backup to the same computer and the same remote shared folder, you will overwrite your previous backup." And: "If your backup operation fails, you can end up without a backup because the older backup is overwritten, but the newer backup isn't usable."</li>
<li><strong>One location.</strong> <code>wbadmin enable backup</code>: "You can only specify one shared folder as a storage location at a time."</li>
<li><strong>Credentials.</strong> The <code>-user</code> you supply needs write permission to the folder. With <code>-noInheritAcl</code>, only those credentials, Administrators, or Backup Operators on the file server can read the backup.</li>
</ul>
<pre><code class="language-powershell"># Read-only. Can this server reach and list the share, as the account you're logged on with?
Test-Path -Path '\\NAS01\WSB'
Get-ChildItem -Path '\\NAS01\WSB\WindowsImageBackup' -ErrorAction SilentlyContinue | Select-Object Name, LastWriteTime

# Read-only. SMB client cache and session timeout values (names from Microsoft's tuning page and DPM KB)
Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters' -ErrorAction SilentlyContinue |
  Select-Object DirectoryCacheLifetime, SessTimeout
</code></pre>
<p><code>Test-Path</code> runs as you, not as the backup account, so a pass here doesn't prove the backup's credentials can write (our note).</p>
<table>
<thead>
<tr>
<th>Share finding</th>
<th>Source</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Event 517 with <code>0x80070012</code> ("There are no more files") on a share target</td>
<td>A Microsoft Japan Windows support team blog post (May 2025) ties it to a stale SMB client directory cache, and reports that setting <code>DirectoryCacheLifetime</code> to 0 plus a restart avoided it</td>
<td>Step 9, on the backed-up server only</td>
</tr>
<tr>
<td><code>0x8078015B</code> "Windows Backup encountered an error when accessing the remote shared folder." with SMB client event 30809</td>
<td>Microsoft's DPM article: "This issue occurs if the input/output (I/O) latency is greater than the standard SMB time-out (60 seconds)." Written for DPM and Azure Backup Server shares</td>
<td>Look at the file server's storage latency first</td>
</tr>
<tr>
<td>Event 52 or 564</td>
<td>Microsoft's event text: the network target "is not writeable"</td>
<td>Share and NTFS permissions for the backup account</td>
</tr>
<tr>
<td>Old backup gone after a failed run</td>
<td>The overwrite rule above</td>
<td>Subfolders per run, with Microsoft's "twice the space" note</td>
</tr>
</tbody></table>
<p>Microsoft's tuning page lists <code>DirectoryCacheLifetime</code> as "Directory cache timeout in seconds," default 10. It's a client-wide SMB setting, so it affects every share that server uses, not just the backup (our reading).</p>
<hr />
<h2>Step 8: The engine, the schedule, and the catalog (read-only)</h2>
<p>The engine is the <code>wbengine</code> service. Microsoft: "The WBENGINE service is used by Windows Backup to perform backup and recovery operations." Its default startup type is Manual, so it's often stopped between jobs (our reading of Microsoft's services table).</p>
<pre><code class="language-powershell"># Read-only. Feature installed? Engine service state? Schedule task present?
Get-WindowsFeature -Name Windows-Server-Backup
Get-Service -Name wbengine | Select-Object Name, Status, StartType
schtasks /query /tn "\Microsoft\Windows\Backup\Microsoft-Windows-WindowsBackup" /v /fo list

# Read-only. Backup policy settings from Group Policy, if any GPO sets them
Get-ItemProperty -Path 'HKLM:\SOFTWARE\Policies\Microsoft\Windows\Backup\Server' -ErrorAction SilentlyContinue
</code></pre>
<p>The task path comes from the Microsoft Q&amp;A threads in Step 6. The policy registry path is our reading. Microsoft's policy location is <code>Local Computer Policy\Computer Configuration\Administrative Templates\Windows Components\Backup\Server</code>.</p>
<table>
<thead>
<tr>
<th>Finding</th>
<th>What it means (Microsoft unless noted)</th>
<th>Next</th>
</tr>
</thead>
<tbody><tr>
<td>Events 17 / 544, engine could not be contacted</td>
<td>The engine service didn't answer. Microsoft's 544 text: "Please check if engine service is installed and enabled."</td>
<td>Check <code>wbengine</code> isn't Disabled</td>
</tr>
<tr>
<td>Events 18 / 545, schedule settings not found</td>
<td>Microsoft: "Please re-configure scheduled backup."</td>
<td>Re-create the schedule from the read in Step 2</td>
</tr>
<tr>
<td>Events 21 / 528, conflict with Group Policy</td>
<td>A Backup policy setting forbids what the schedule asks for</td>
<td>Change the schedule or the GPO, not both</td>
</tr>
<tr>
<td>WSB won't start after a bare metal restore on UEFI</td>
<td>KB 2961238: Wbengine.exe "does not handle cases correctly when the EFI system partition location is changed during the restore"</td>
<td>The catalog rebuild in Step 9</td>
</tr>
<tr>
<td><code>wbadmin get versions</code> errors, other targets still hold good backups</td>
<td>A damaged local catalog (our reading)</td>
<td><code>wbadmin restore catalog</code> from that target before you delete anything</td>
</tr>
<tr>
<td><code>Get-WBJob</code> errors as an unknown command</td>
<td>Microsoft's Azure page: reinstall the Windows Server Backup feature</td>
<td>The one case where a reinstall is Microsoft's step</td>
</tr>
</tbody></table>
<hr />
<h2>Common false starts (read these before you change anything)</h2>
<table>
<thead>
<tr>
<th>False start</th>
<th>Why it hurts</th>
<th>Read-first alternative</th>
</tr>
</thead>
<tbody><tr>
<td><strong><code>vssadmin delete shadows /all</code> "to free space"</strong></td>
<td>Microsoft: "Deleting a shadow copy is permanent." You lose Previous Versions and anything else that depends on them, and the real shortage may be a 300 MB partition</td>
<td>Step 4: find the volume that's short, and who owns the copies</td>
</tr>
<tr>
<td><strong><code>wbadmin delete catalog</code> first</strong></td>
<td>Microsoft: after you delete it, "you'll no longer be able to get to any backups created for that computer using the Windows Server Backup snap-in." Microsoft says to use it only when the catalog is corrupted and can't be restored</td>
<td>Step 8: <code>wbadmin restore catalog</code> from a target that still has backups</td>
</tr>
<tr>
<td><strong>Reinstalling the Windows Server Backup feature</strong></td>
<td>Doesn't touch VSS, space, the ESP, or the share. Microsoft's reinstall step is for a missing <code>Get-WBJob</code></td>
<td>Steps 2-7</td>
</tr>
<tr>
<td><strong><code>-vssFull</code> while another product backs up SQL or Exchange on that server</strong></td>
<td>Microsoft: "Don't use this parameter if you are using a product other than Windows Server Backup to back up apps that are on the volumes included in the current backup." It can break that product's log chain</td>
<td>Keep the default <code>-vssCopy</code></td>
</tr>
<tr>
<td><strong><code>fsutil usn deletejournal</code> on System Reserved because a forum said so</strong></td>
<td>Microsoft's caution: deleting the change journal "impacts the File Replication Service (FRS) and the Indexing Service" and forces a complete rescan of the volume</td>
<td>Step 4: read the partition's free space and its contents first</td>
</tr>
<tr>
<td><strong><code>/MaxSize=UNBOUNDED</code> on every volume</strong></td>
<td>Shadow storage can then grow until the volume is full (our reading of "no limit" in Microsoft's syntax)</td>
<td>A sized maximum on a volume with room</td>
</tr>
<tr>
<td><strong>Disabling VSS</strong></td>
<td>Microsoft: "you shouldn't do this. Disabling VSS adversely affects any software you use that depends on it, such as System Restore and Windows Server Backup."</td>
<td>Fix the failed writer or the space</td>
</tr>
<tr>
<td><strong>Turning off AV or EDR for every backup</strong></td>
<td>Leaves the server unprotected every night, and the ESP lock may come from another program</td>
<td>Step 6: find what holds the ESP, then a vendor-documented exclusion</td>
</tr>
<tr>
<td><strong>A retry task with nobody watching</strong></td>
<td>Hides a real, repeating failure behind a second run (our reading)</td>
<td>Keep it only for the intermittent ESP pattern, and keep reading events</td>
</tr>
</tbody></table>
<hr />
<h2>Step 9: Change one layer, then watch (only after the read)</h2>
<p>Pick the one change Steps 1-8 point at. Most of these tools have no dry-run switch, so the read in Steps 2-8 is your dry run. Export a registry key before you change it:</p>
<pre><code class="language-powershell"># Low-risk write. Save the VSS and SMB client keys before you change them (our habit)
reg export HKLM\SYSTEM\CurrentControlSet\Services\VSS C:\Temp\vss-before.reg /y
reg export HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters C:\Temp\lanmanws-before.reg /y
</code></pre>
<p><strong>A source volume short on space (Step 4).</strong> Free space on that volume first. For the shadow copy storage itself, Microsoft's command, after <code>vssadmin list shadowstorage</code>:</p>
<pre><code class="language-powershell"># Change. Cap shadow storage for C: on C: at 20% (Microsoft's example form). No dry-run switch
vssadmin resize shadowstorage /For=C: /On=C: /MaxSize=20%
</code></pre>
<p>Microsoft's warning: "Resizing the storage association may cause shadow copies to disappear." Read <code>vssadmin list shadows</code> first and decide whether you can lose them. For a hidden recovery or System Reserved partition under Microsoft's minimum, growing it means repartitioning, which is its own change window (our reading). If it isn't a critical volume, excluding it from a volume backup is the smaller change.</p>
<p><strong>Delete only the copies you've identified (Step 4).</strong> Microsoft's form targets one ID, after you confirm it with <code>vssadmin list shadows</code>:</p>
<pre><code class="language-powershell"># Change. Permanent. One client-accessible shadow copy, by ID
vssadmin delete shadows /for=c: /shadow=&lt;ShadowID&gt;
</code></pre>
<p>Microsoft: "You can only delete shadow copies with the client-accessible type." Copies outside that context need <code>diskshadow</code>, and they usually belong to some other product (our reading).</p>
<p><strong>A failed SQL writer (Step 5).</strong> Microsoft's KB test: run one backup with the named instance stopped, in a window where that's acceptable. The service name below is an example:</p>
<pre><code class="language-powershell"># Change, in a maintenance window. Dry-run first; MSSQL$FINANCE is an example instance
Stop-Service -Name 'MSSQL$FINANCE' -WhatIf
</code></pre>
<p>If the backup completes with it stopped, Microsoft says to "examine the SQL Server error log files and the event logs" for that instance. Fix the database, then start it again. Don't leave it stopped as the "fix."</p>
<p><strong>System Writer missing, CAPI2 513 confirmed (Step 5).</strong> Microsoft's KB 2009272 resolution, unchanged:</p>
<pre><code class="language-console">Takeown /f %windir%\winsxs\temp\PendingRenames /a
icacls %windir%\winsxs\temp\PendingRenames /grant "NT AUTHORITY\SYSTEM:(RX)"
icacls %windir%\winsxs\temp\PendingRenames /grant "NT Service\trustedinstaller:(F)"
icacls %windir%\winsxs\temp\PendingRenames /grant "BUILTIN\Users:(RX)"
Takeown /f %windir%\winsxs\filemaps\* /a
icacls %windir%\winsxs\filemaps\*.* /grant "NT AUTHORITY\SYSTEM:(RX)"
icacls %windir%\winsxs\filemaps\*.* /grant "NT Service\trustedinstaller:(F)"
icacls %windir%\winsxs\filemaps\*.* /grant "BUILTIN\Users:(RX)"
net stop cryptsvc
net start cryptsvc
</code></pre>
<p>Then <code>vssadmin list writers</code> should show <code>System Writer</code>. If it's still missing and VSS event 8213 names NETWORK SERVICE, the same KB sets <code>NT AUTHORITY\NETWORK SERVICE</code> (REG_DWORD) to 1 under <code>HKLM\SYSTEM\CurrentControlSet\Services\VSS\VssAccessControl</code>. The PowerShell form is ours, with a real <code>-WhatIf</code>:</p>
<pre><code class="language-powershell"># Change. Dry-run first; export the VSS key above before you run it for real
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\VSS\VssAccessControl' -Name 'NT AUTHORITY\NETWORK SERVICE' -Value 1 -Type DWord -WhatIf
</code></pre>
<p><strong>The ESP lock (Step 6).</strong> Work through it in this order: find the program that holds the ESP, then apply that vendor's documented exclusion or move its schedule away from the backup. Microsoft's Azure page also says to let a running third-party backup finish first. If it's still intermittent after that, the Microsoft Q&amp;A workaround is a task that reruns the backup when event 517 appears. It's community-posted, so test it on one server and keep reading the events:</p>
<pre><code class="language-console">schtasks /create /F /RU SYSTEM /RL HIGHEST /SC onevent /EC Application /MO "*[System[Provider[@Name='Microsoft-Windows-Backup'] and (EventID=517)]]" /TN "Microsoft\Windows\Backup\Retry_Backup" /TR "schtasks /run /tn \"Microsoft\Windows\Backup\Microsoft-Windows-WindowsBackup\""
</code></pre>
<p>The thread also enables "Allow task to be run on demand" on the Microsoft-Windows-WindowsBackup task first. Our caution: event 517 is also the generic failure event, so this task reruns every kind of failure, not just the ESP one.</p>
<p><strong>A share target (Step 7).</strong> For overwrites, give each run its own subfolder, sized for Microsoft's "twice the space" note, or move to a dedicated disk. For the <code>0x80070012</code> pattern, the Microsoft Japan support blog's workaround is a client-wide SMB change, so apply it to the backed-up server only, then restart:</p>
<pre><code class="language-powershell"># Change, on FS01 only. Dry-run first; Microsoft's tuning page default is 10 seconds
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters' -Name DirectoryCacheLifetime -Value 0 -Type DWord -WhatIf
</code></pre>
<p>For <code>0x8078015B</code> against a DPM or Azure Backup Server share, Microsoft's KB adds <code>SessTimeout</code> = 1200 under the same key and restarts the server or the Workstation service. Our reading: fix the file server's storage latency first, and use the timeout only for that DPM case.</p>
<p><strong>The catalog after a bare metal restore (Step 8).</strong> Microsoft's KB 2961238 resolution: run <code>wbadmin delete catalog</code> from an elevated prompt, then restart the Windows Server Backup service or the computer. That's the documented case. Anywhere else, try <code>wbadmin restore catalog</code> from a target that still holds backups first, and take a new backup right after, as Microsoft's delete catalog page recommends.</p>
<p><strong>Then prove it with one run.</strong> With no parameters, <code>wbadmin start backup</code> uses the scheduled backup's settings, so it tests the same job that failed. For a domain controller's system state, Microsoft's command:</p>
<pre><code class="language-powershell"># Change (it writes a backup). Rerun the scheduled job's settings now
wbadmin start backup

# Change (it writes a backup). A DC system state test to a separate volume
wbadmin start systemstatebackup -backupTarget:E: -quiet
</code></pre>
<p>Read-only confirm afterwards:</p>
<pre><code class="language-powershell"># Read-only. Last job result, the new version, writer state, and the success event
Get-WBJob -Previous 1
wbadmin get versions -backupTarget:E:
vssadmin list writers
Get-WinEvent -FilterHashtable @{ LogName = 'Application'; ProviderName = 'Microsoft-Windows-Backup'; Id = 4 } -MaxEvents 3 -ErrorAction SilentlyContinue |
  Select-Object TimeCreated, Message
</code></pre>
<p>Watch one full scheduled night too. Event 4 ("Backup finished successfully."), a new version on the target, every writer <code>[1] Stable</code>, and nothing new from VSS or VolSnap. Then restore one file from the new version with <code>wbadmin get items -version:&lt;VersionIdentifier&gt;</code> and a test recovery. A backup that has never been restored is a guess (our view).</p>
<hr />
<h2>When a change is justified</h2>
<table>
<thead>
<tr>
<th>Finding</th>
<th>Prefer</th>
<th>Avoid</th>
</tr>
</thead>
<tbody><tr>
<td>Hidden partition under Microsoft's free-space minimum</td>
<td>Free or grow that partition in a change window; exclude it if it isn't critical</td>
<td>Deleting every shadow copy on the server</td>
</tr>
<tr>
<td>Shadow storage capped too low</td>
<td><code>vssadmin resize shadowstorage</code> after reading <code>list shadows</code></td>
<td><code>UNBOUNDED</code> on a nearly full volume</td>
</tr>
<tr>
<td>SQL writer failing</td>
<td>Test with that instance stopped, then fix the database</td>
<td>Leaving SQL stopped, or <code>-vssFull</code> next to another product</td>
</tr>
<tr>
<td>System Writer missing, CAPI2 513</td>
<td>Microsoft's KB 2009272 permissions, then <code>VssAccessControl</code> if 8213</td>
<td>Reinstalling the feature</td>
</tr>
<tr>
<td>ESP lock every night</td>
<td>Find the program holding the ESP, then a vendor exclusion</td>
<td>AV off every night</td>
</tr>
<tr>
<td>Share target overwritten</td>
<td>Subfolders or a dedicated disk</td>
<td>Same folder every night</td>
</tr>
<tr>
<td><code>0x80070012</code> to a share</td>
<td><code>DirectoryCacheLifetime</code> 0 on the backed-up server only</td>
<td>Changing it fleet-wide</td>
</tr>
<tr>
<td>WSB won't start after a UEFI bare metal restore</td>
<td><code>wbadmin delete catalog</code> per KB 2961238, then a new backup</td>
<td>Deleting the catalog for any other error</td>
</tr>
</tbody></table>
<hr />
<h2>FAQ</h2>
<p><strong>Why does Windows Server Backup fail to create a shadow copy?</strong>
Usually one of three things: a source volume without enough free space for its shadow copy (Microsoft's minimums are 50 MB on volumes under 500 MB and 320 MB on larger ones), a VSS writer in a failed state, or a timeout under load. Run <code>vssadmin list writers</code> right after the failure, read the VSS events, then check free space on every partition in the backup, including the hidden ones.</p>
<p><strong>Why does Windows Server Backup say not enough space when the backup disk has plenty?</strong>
Because the message is usually about the source. Microsoft says the shadow copy space rule "applies to both the backup storage destination and volumes included in the backup." Check every partition with <code>Get-Partition</code> and <code>Get-Volume</code>, and check shadow storage with <code>vssadmin list shadowstorage</code>. Event 50 is the one that means the target itself is full.</p>
<p><strong>How do I fix "Windows Backup failed to get an exclusive lock on the EFI system partition"?</strong>
Find what else is touching the ESP during the backup. Microsoft's Azure Backup page names third-party security or backup software. Get a documented exclusion from that vendor, or move the other job's schedule. If it stays intermittent, Microsoft Q&amp;A threads describe a task that reruns the backup on event 517. Treat that as a workaround and keep reading the events.</p>
<p><strong>Where are the Windows Server Backup logs and event IDs?</strong>
The Application log, source <code>Microsoft-Windows-Backup</code> (517 for a failure with a code, 521 for a VSS failure, 4 for success), plus the <code>Microsoft-Windows-Backup</code> channel. System state jobs also write text logs under <code>C:\Windows\Logs\WindowsServerBackup</code>. Then read the layer the event names, in that layer's own log.</p>
<p><strong>Can Windows Server Backup keep multiple versions on a network share?</strong>
Not in one folder. Microsoft: another backup of the same computer to the same remote shared folder "will overwrite your previous backup," and a failed run can leave nothing usable. Use subfolders, sized for about twice the space, or a local backup disk, which doesn't carry that overwrite rule (our reading of Microsoft's remote-folder remarks).</p>
<hr />
<h2>Related posts</h2>
<ul>
<li><a href="https://dev.to/adminpackstudio/hyper-v-checkpoint-operation-failed-a-read-first-vss-storage-permissions-and-orphaned-avhdx-5ep">Hyper-V Checkpoint Operation Failed</a> (guest VSS, the Hyper-V VSS writer, and restarting VSS and swprv when a VM is the failing item)</li>
<li><a href="https://dev.to/adminpackstudio/sql-server-transaction-log-full-error-9002-a-read-first-logreusewaitdesc-log-backup-and-1i91">SQL Server Transaction Log Full (Error 9002)</a> (the log-backup side when a backup product and SQL disagree about who truncates logs)</li>
<li><a href="https://dev.to/adminpackstudio/retention-is-not-backup-what-microsoft-365-can-restore-natively-and-a-quarterly-restore-drill-4j2p">Retention Is Not Backup</a> (the restore-drill habit that proves a backup works before you need it)</li>
</ul>
<h2>Free checklist</h2>
<p>A backup nobody has test-restored is a change nobody piloted. So is a Conditional Access policy enforced before anyone read the report-only results. The free <strong>Conditional Access pre-enforcement checklist</strong> keeps break-glass accounts, pilot groups, and report-only review on one page:</p>
<p>👉 <a href="https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-server-backup-failed">https://cashflow4375.gumroad.com/l/ujkqxb?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-server-backup-failed</a></p>
<p>If you want the rest of the Intune runway written down for the team (enrollment hygiene, compliance phasing, break/fix cards and read-only PowerShell snapshots), the <strong>Intune &amp; M365 Admin Starter Pack</strong> pairs with this triage: <a href="https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-server-backup-failed">https://cashflow4375.gumroad.com/l/joonf?utm_source=hashnode&amp;utm_medium=article&amp;utm_campaign=aps_first_sale&amp;utm_content=windows-server-backup-failed</a></p>
<hr />
<p><em>Admin Pack Studio; not affiliated with Microsoft. Steps 1-8 favor read-only checks: <code>Get-WBSummary</code>, <code>Get-WBJob</code>, <code>Get-WBPolicy</code>, <code>Get-WBBackupTarget</code>, <code>Get-WBDisk</code>, <code>Get-WBVolume</code>, <code>wbadmin get versions</code> / <code>get disks</code> / <code>get status</code>, <code>Get-WinEvent</code>, <code>vssadmin list writers</code> / <code>providers</code> / <code>shadows</code> / <code>shadowstorage</code> / <code>volumes</code>, <code>Get-Volume</code>, <code>Get-Partition</code>, <code>Get-Service</code>, <code>Get-WindowsFeature</code>, <code>whoami /groups</code>, <code>icacls</code> (display only), <code>Test-Path</code>, <code>Get-ChildItem</code>, <code>Get-ItemProperty</code>, and <code>schtasks /query</code>. <code>reg export</code> writes local files. Resizing shadow storage, deleting shadow copies, stopping SQL Server, the KB 2009272 permission changes, <code>VssAccessControl</code>, <code>DirectoryCacheLifetime</code>, <code>SessTimeout</code>, retry tasks, deleting or restoring the catalog, and running a backup are deliberate changes. Change one layer at a time, test on one server first, and check current Microsoft Learn for version-specific behavior.</em></p>

]]></content:encoded></item></channel></rss>