Systems Admin

Disable the Print Spooler on Domain Controllers (PrintNightmare Hardening via GPO)

The Print Spooler service (spoolsv.exe) runs by default on every Windows host. It manages print jobs, talks to printers, and historically has been one of the most fertile sources of remote-code-execution vulnerabilities in the Microsoft codebase. PrintNightmare (CVE-2021-1675 + CVE-2021-34527) was the most famous, but it’s far from alone — SpoolFool (CVE-2022-21999), CVE-2022-22717, CVE-2024-38198, and a steady drumbeat of smaller bugs across the years. The service runs as SYSTEM. On a Domain Controller, RCE-as-SYSTEM is one privilege escalation away from Domain Admin. Disabling Print Spooler on every DC is a no-brainer hardening step that costs nothing (DCs don’t need to print) and closes off the entire class of vulnerability. This post walks the GPO end-to-end.

Why this is a no-brainer

Question Answer
Does a DC need to print? No.
What breaks if Print Spooler is disabled on a DC? Nothing.
What does Print Spooler enable on a DC? An RCE-as-SYSTEM attack surface that history has shown is exceptionally hard to keep bug-free.
How long does this take to roll out? ~10 minutes for the GPO + push.
Reversible if needed? Yes — remove the GPO link or set the policy to Not Configured.

This is the rare hardening change with no downside. Microsoft’s own security baseline for Windows Server recommends it. CIS Benchmarks recommend it. Every red-team engagement against a Microsoft AD environment looks at the spooler first. Just turn it off.

The vulnerability history (in case you need to convince anyone)

  • CVE-2021-1675 / CVE-2021-34527 (PrintNightmare, 2021). The headliner. RemoteCode execution as SYSTEM via the RPC interface of the spooler. Patches were issued, then bypassed, then patched again, then bypassed again. Several public PoCs that any low-priv user on the domain could weaponise.
  • CVE-2022-21999 (SpoolFool, 2022). Local privilege escalation via spooler. Useful in chains where attacker has any code execution on a host and wants to escalate to SYSTEM.
  • CVE-2022-22717. Another spooler RCE, slightly different mechanism.
  • CVE-2024-38198. Information disclosure in spooler — less severe but useful for attacker reconnaissance.
  • Multiple CVE-XXXX-YYYY each year through the entire history of Windows Server that involve spooler in some way. The codebase is old, complex, and has been an attacker focus for years.

If your security team needs justification, this list is it. The pattern is: a new spooler bug ships, Microsoft patches it, attackers find a bypass, Microsoft patches the bypass, repeat. Disabling the service ends the cycle for hosts that don’t need it.

Prerequisites

  • Domain Admin (to create and link a GPO at the Domain Controllers OU).
  • Group Policy Management Console (gpmc.msc) installed — either on a DC or a management workstation with RSAT.
  • Awareness of which Domain Controllers (if any) are also acting as print servers. If any DC IS a print server, deal with that first — move the print queues to a dedicated print server (which is best practice anyway), THEN apply this GPO.

Step 1 — verify the current state on a DC

Before changing anything, look at the as-is state. Log into a Domain Controller. Open Services (services.msc).

Services console (services.msc) on a Domain Controller showing the Print Spooler service in the Running state with Startup type Automatic, the as-shipped default state that this post sets out to change
Starting state on a Domain Controller out of the box. Print Spooler runs as SYSTEM, starts automatically on boot, and stays running indefinitely. Every Windows host ships this way; until you intervene, every DC in the forest is exposed to whatever the next spooler vulnerability turns out to be.

Find Print Spooler. Confirm:

  • Status: Running
  • Startup type: Automatic

This is the default. Every DC ships this way. If yours shows differently, someone has already partially hardened — check Event Viewer for previous policy applications before assuming you’re starting from a clean state.

Step 2 — create and link the GPO

From a DC or a management workstation with GPMC installed:

  1. Open Group Policy Management Console (gpmc.msc).
  2. Expand Forest > Domains > your domain name.
  3. Right-click the Domain Controllers Organizational Unit (the built-in OU; don’t create a new one).
  4. Select Create a GPO in this domain, and Link it here…
  5. Name it descriptively. Suggested: GPO_Disable_PrintSpooler_DCs. Click OK.
  6. Click on the newly created GPO > right-click > Edit. Group Policy Management Editor opens.
Group Policy Management Console with the Domain Controllers OU expanded and a newly created GPO named GPO_Disable_PrintSpooler_DCs linked to it, the policy object that will carry the System Services configuration
Step 1 — create a GPO scoped to the Domain Controllers OU. Right-click the OU > Create a GPO in this domain, and Link it here. Naming convention: prefix with GPO_ + describe + scope (e.g. GPO_Disable_PrintSpooler_DCs) so the policy is self-documenting in GPMC views.

Naming convention matters in environments with many GPOs. GPO_Disable_PrintSpooler_DCs tells you at a glance: it’s a custom GPO (GPO_ prefix), what it does (Disable_PrintSpooler), and where it applies (DCs). Self-documenting names save investigation time later.

Step 3 — configure the System Services policy

In Group Policy Management Editor, navigate to:

Computer Configuration
└─ Policies
   └─ Windows Settings
      └─ Security Settings
         └─ System Services

The right pane lists every Windows service the GPO can manage. Scroll down to Print Spooler and double-click.

Group Policy Management Editor with Computer Configuration > Windows Settings > Security Settings > System Services selected and the Print Spooler row showing Startup Mode set to Disabled with the policy explicitly defined” /><figcaption>Step 2 — the actual policy. Computer Configuration > Windows Settings > Security Settings > System Services. Find Print Spooler in the right pane. Tick “Define this policy setting” and pick Disabled. The Disabled state is the load-bearing choice — Manual would let it be started on demand; Automatic would defeat the purpose.</figcaption></figure>
<p>In the dialog:</p>
<ol>
<li>Tick <strong>Define this policy setting</strong>.</li>
<li>Select the <strong>Disabled</strong> radio button.</li>
<li>Click OK.</li>
</ol>
<p><strong>Disabled, not Manual.</strong> Manual lets the service be started on demand by anything that triggers a print operation. Disabled prevents start entirely — which is what you want for a service you’ve decided shouldn’t exist on this host.</p>
<p>The GPMC view now shows the service as Disabled.</p>
<h2>Step 4a — force update on a single DC for testing</h2>
<p>Always test on one DC before rolling out to all. Pick a non-critical DC (a secondary in a less-busy site) for the first apply.</p>
<p>Log into that DC. Open Command Prompt as Administrator:</p>
<pre><code>gpupdate /force</code></pre>
<p>Wait for both User policy and Computer policy to report “completed successfully.” The Computer policy is the one that matters here; User policy is unaffected by this GPO.</p>
<figure class=Command Prompt window on a Domain Controller after running gpupdate /force showing the User policy and Computer policy update successfully completed messages, confirming the new GPO has been pulled and applied to this DC
Step 3a — force the policy on a single DC for testing. Open elevated cmd, run gpupdate /force, wait for the Computer policy update successfully completed message. Use this on the first DC to verify the policy lands cleanly before pushing to the whole OU.

Verify on this DC: Services console > Print Spooler. Status should now be Stopped, Startup type Disabled. If yes, proceed to Step 4b for the rest of the DCs. If no, troubleshoot before pushing to fleet.

Step 4b — push to all DCs via GPMC (recommended)

Once the test DC is good, push to the whole fleet:

  1. Open GPMC.
  2. Right-click the Domain Controllers OU.
  3. Select Group Policy Update…
  4. Confirm the prompt about forcing the update on all computers in the scope. Click Yes.

A results window shows the per-DC status. Each DC should report success.

Group Policy Management Console showing the Force Group Policy Update dialog after right-clicking the Domain Controllers OU > Group Policy Update, with the per-DC results pane showing each Domain Controller responding to the forced refresh” /><figcaption>Step 3b — push to all DCs. Right-click the Domain Controllers OU in GPMC > Group Policy Update > confirm. The dialog shows per-DC results so you can spot any controller that didn’t respond. Cleaner than running gpupdate manually on every DC; ideal for a fleet of more than 2–3 DCs.</figcaption></figure>
<p>This is cleaner than running <code>gpupdate /force</code> on every DC manually — especially for environments with more than a handful of controllers.</p>
<h2>Step 5 — final verification across the fleet</h2>
<p>Pick a couple of DCs at random and confirm:</p>
<figure class=Services console on the same Domain Controller after the policy applied showing the Print Spooler service in the Stopped state with Startup type Disabled, the desired end state with the service controlled by GPO and unable to start
Step 4 — verification. services.msc on the DC after the policy applied. Print Spooler shows Stopped + Disabled. The Startup type field will be greyed out because the GPO controls it; users (even local admins) cannot change it through services.msc anymore. If status is still Running after gpupdate, reboot the DC to be safe.

Services > Print Spooler:

  • Status: Stopped
  • Startup type: Disabled (and the field is greyed out — controlled by GPO)

The greyed-out field is the visible signature of GPO control. Local admins cannot change it through services.msc; the only way to start the service is to remove the GPO link or change the policy.

If a DC still shows the service as Running:

  1. Confirm the GPO actually applied: gpresult /h C:\gpresult.html on that DC, then open the HTML report and look for the GPO_Disable_PrintSpooler_DCs entry.
  2. If applied but status still Running, a dependency or another service is restarting it — reboot the DC. Print Spooler should not start at next boot.
  3. If GPO didn’t apply: check OU placement (the DC must be in an OU under or at Domain Controllers), check security filtering on the GPO, check Event Viewer at Application logs > Group Policy.

Things that bite people

A DC that’s also a print server

If you have any DC acting as a print server, this GPO breaks printing for whoever depends on that DC. Solution: move print queues to a dedicated print server (best practice anyway) BEFORE applying the GPO. If for some reason a specific DC genuinely must keep the spooler running, exclude it via security filtering on the GPO — but treat that exception as a security debt to pay down.

The policy applies to non-DC servers under DC OU

If you’ve placed non-DC servers in or under the Domain Controllers OU (rare but happens), this GPO disables their spooler too. Verify OU contents before pushing. Either move non-DCs out, or scope the GPO via security filtering to apply only to DC computer accounts.

Confusion between “Disabled” and “Not Configured”

In GP Editor, the System Services dialog has three states: Not Defined (the policy doesn’t affect the service), Disabled (force the service to Disabled startup type), and Manual (force to Manual startup). Pick Disabled. Not Defined leaves the service alone — useless for hardening.

Local admin tries to start the spooler

After the GPO applies, a local admin on the DC may try to start the spooler manually for some operational reason. Services.msc shows the field greyed out; PowerShell Start-Service Spooler fails with a permission error. The local admin can’t override GPO without first removing the GPO link — which requires Domain Admin. This is the desired behaviour but can confuse people.

Patching the rest of the fleet

Disabling spooler on DCs is the highest-leverage move. The same logic applies to ANY server that doesn’t print: file servers, app servers, database servers, web servers. Consider extending the GPO to cover server OUs beyond Domain Controllers. The trade-off: someone on those servers will eventually need to print something and discover the policy. For DCs the trade-off is none; for app servers it’s minimal-but-nonzero.

The spooler keeps coming back after a Windows update

Some Windows Server cumulative updates re-enable services as part of their installation. The GPO re-applies on the next gpupdate cycle (default 90 minutes for non-DCs, ~5 minutes for DCs), so the re-enabling is short-lived. If you want belt-and-suspenders: also set the spooler service to Disabled in a GPO Preferences > Services entry, which applies more aggressively than the System Services policy.

RPC over HTTP gets caught up too

Spooler is one of several RPC-exposed services on a DC. If you’re looking at this post for hardening, also consider: disabling SMBv1 (very old vulnerability source), restricting NTLM where possible, enforcing LDAP signing/binding (covered in other posts in the AD hardening pathway). The spooler GPO is one item in a larger hardening checklist.

Print Spooler dependencies on hosts that DO print

For non-DC hosts that legitimately print, the spooler service has dependencies (Network Printer Discovery and Sharing has been the answer to most weird printer issues over the years). Disabling spooler on a server that needs to print breaks more than just the print operation. Don’t apply this GPO to OUs containing print servers or end-user workstations.

Where this fits

This is one of the most-bang-for-buck hardening operations in Active Directory. Combined with other DC hardening posts in the Group Policy pathway and the broader AD hardening posts in the Active Directory pathway, it forms part of a baseline that every domain should have applied. For Microsoft’s own broader recommendations, see the Windows Server Security Baseline (downloadable as a set of GPOs from Microsoft Security Compliance Toolkit).

Leave a Reply