PKI & Certificates /
AD CS Health Check Script v1.3: Multi-Server, OCSP & WinRM-Free Remote Checks
Last Updated: July 2026
Answer capsule: AD CS Health Check Script v1.3 adds three things: it checks multiple issuing CAs in one combined report, it monitors OCSP responder health, and it no longer needs WinRM for remote registry reads. As a result, the script now runs cleanly from a jumpbox even when ports 5985/5986 are firewalled between sites.
Table of Contents
What’s New in v1.3
If you’re already running the original AD CS Health Check Script, v1.3 is a direct upgrade. In my testing across a two-CA lab, the update touches three areas: how many servers you can check at once, how the script talks to those servers remotely, and what it monitors on each one.
Download the Script from GitHub
1. Multi-server reporting
The old script checked one CA per run. The new -CAServers parameter accepts a list, so one run now produces a single HTML report with a collapsible section per server, plus a dropdown filter to isolate one server’s findings. The old -CAServer (singular) parameter still works, so existing scheduled tasks don’t break.
2. Registry reads no longer need WinRM
Version 1.2 used Invoke-Command for remote registry reads, which requires WinRM (TCP 5985/5986). That’s the single most common reason the original script failed against remote CAs. Version 1.3 replaces those specific calls with .NET’s OpenRemoteBaseKey method against the Remote Registry service, which travels over RPC instead.
3. OCSP responder monitoring
Every issuing CA now gets an OCSP section. The script reads the CA’s AIA configuration, extracts any URL flagged for OCSP, and tests whether that endpoint actually answers. There’s also an opt-in deep verification mode for a full live chain check.
Why the WinRM Fix Actually Matters
A common mistake I see in health-check scripts is treating Invoke-Command as the default way to reach a remote server. It’s convenient, but it silently adds a dependency: WinRM must be enabled, the firewall rule for it must be open, and the account running the script needs to be in the remote management group on the target.
Consequently, if a site-to-site firewall blocks 5985/5986 but allows RPC and SMB — which is common between hardened CA subnets and a jumpbox — the v1.2 script would fail its registry reads even though everything else about the CA was healthy. Because v1.3 moves those specific calls to the Remote Registry service over RPC, the same jumpbox now needs one fewer open port to get a full picture.
It’s worth being precise here: this isn’t a full “no RPC/SMB needed” rewrite. Several checks were already RPC- or SMB-based before v1.3 (service status, event logs, CRL files over the c$ share). The v1.3 change closes the one gap that was specifically WinRM-dependent.
Remote Requirements Without WinRM
Here’s what actually needs to reach each issuing CA when you run the script from a management server or jumpbox:
| Check Area | Transport | Port(s) |
|---|---|---|
| CA config via certutil | RPC | 135 + dynamic |
| Service/event/WMI queries | RPC/DCOM | 135 + dynamic |
| Registry reads (Active CA, DB paths) | Remote Registry service | RPC (no WinRM) |
| CRL file checks | SMB (c$ admin share) | 445 |
| OCSP endpoint reachability | HTTP/HTTPS | 80/443 |
If any of these paths are also blocked between your jumpbox and a given CA, the script doesn’t die — the affected check degrades to a Warning finding for that server, and the rest of the run continues. If your sites are fully segmented from each other, run the script locally on (or from a jumpbox in) each site instead of trying to reach across.
OCSP Monitoring, Explained
How does the script find your OCSP responder without you telling it the URL? It reads the CA’s AIA configuration and looks for any entry whose publication flags include the value 32, which is the flag Microsoft defines specifically for including a URL in the OCSP extension of issued certificates.
For each URL it finds, the script sends a plain HTTP GET. Any response — even an HTTP 400 or 405 — counts as Healthy, because it proves something is listening at that address. Only a connection failure (timeout, refused, DNS failure) is marked Critical. This matters because OCSP responders often reject a bare GET as malformed while still being perfectly healthy for real OCSP POST requests from clients.
If you run a dedicated Online Responder separate from the CA, pass its name with -OCSPServers so the script also checks the OCSPSvc service directly. If you skip that parameter, it checks the service on the CA servers themselves, which fits a co-located responder setup.
The optional deep verification
The default OCSP check only confirms the endpoint answers. If you want an actual chain validation — confirming a certificate can be built and its revocation status resolved end to end — add -DeepOCSPVerify. This runs certutil -verify -urlfetch against the CA certificate, which pulls AIA, CDP, and OCSP data live over the network. It’s slower and depends on outbound reachability, so I recommend running it as a separate, less frequent check rather than in every scheduled run.
A Read-Only Correction: CRL Publish Is Now Opt-In
This is worth calling out directly if you’re running the previous version in production: v1.2’s CRL health check ran certutil -CRL, which doesn’t just read the CRL — it actively publishes a new one. That’s a low-risk action on a healthy CA, but it is not read-only, and a script marketed as a health check shouldn’t take a write action by default.
Version 1.3 fixes this. The default run only reads existing CRL files from the CertEnroll share and reports their expiry. Actually publishing a test CRL is now behind the explicit -TestCRLPublish switch, so nothing changes on the CA unless you ask for it.
Running the Multi-Server Report
A typical run against two issuing CAs and a dedicated Online Responder looks like this:
.\ADCSHC_v1.3.ps1 -CAServers "ICA01","ICA02" -OCSPServers "OCSP01","OCSP02" -ExportPath "C:\Reports"
Each server gets its own collapsible section with a healthy/warning/critical/info pill count in the header, so you can scan overall status before opening any section. The search box and status filter buttons at the top apply across all servers at once; the server dropdown narrows the view to just one.
Recommended flags for a jumpbox-based run
| Parameter | When to use it |
|---|---|
-CAServers |
Always, for more than one issuing CA |
-OCSPServers |
Only if your Online Responder is a separate box from the CA |
-DeepOCSPVerify |
Weekly spot-checks, not every scheduled run |
-TestCRLPublish |
Change windows only — it actively publishes a CRL |
Remote Check Architecture
The diagram below shows how a jumpbox reaches multiple CAs and OCSP responders without WinRM in the path.
JumpboxADCSHC_v1.3.ps1ICA01ICA02OCSP01RPC 135+dyn, SMB 445RPC 135+dyn, SMB 445HTTP/HTTPS 80/443WinRMnot required
Troubleshooting Common Remote Errors
| Symptom | Likely Cause | Fix |
|---|---|---|
| “Could not read Active CA from registry” warning | Remote Registry service stopped, or RPC blocked | Start Remote Registry service on the CA; confirm TCP 135 + dynamic RPC is open |
| “CertEnroll folder not accessible” warning | SMB 445 or the c$ admin share is blocked | Open TCP 445 to the CA, or run the script locally on the CA instead |
| OCSP row shows Critical for every URL | No route from jumpbox to the OCSP responder over HTTP/HTTPS | Open TCP 80/443 from the jumpbox, or check the Online Responder’s IIS bindings |
| “No OCSP URL found in the CA’s AIA configuration” | No AIA entry carries flag 32 | Add the Online Responder URL to the AIA extension with the OCSP option enabled, then reissue as needed |
Frequently Asked Questions
What does AD CS Health Check Script v1.3 change from v1.2?
It adds multi-server reporting via -CAServers, replaces WinRM-dependent registry reads with the Remote Registry service over RPC, adds an OCSP monitoring section, and makes the CRL publish test opt-in instead of running by default.
Does v1.3 still need WinRM at all?
No. The registry reads that previously required WinRM now use OpenRemoteBaseKey against the Remote Registry service, which travels over RPC. Every other check in the script was already RPC-, DCOM-, or SMB-based.
Will my existing v1.2 scheduled task break?
No. The singular -CAServer parameter is still supported for backward compatibility and is merged into the new -CAServers list internally.
What ports does the script need to reach a remote CA?
TCP 135 plus the dynamic RPC range for service, event, and registry checks, and TCP 445 for CRL file access over the admin share. OCSP checks additionally need outbound HTTP/HTTPS (80/443).
Is the default run genuinely read-only now?
Yes. In v1.2, the CRL health check ran certutil -CRL, which publishes a new CRL as a side effect. In v1.3, that action is opt-in via -TestCRLPublish, so a default run only reads data.
How does the script find my OCSP responder URL?
It parses the CA’s AIA configuration (CACertPublicationURLs) and looks for entries whose publication flags include 32, the flag Microsoft assigns for OCSP inclusion.
What does an HTTP 400 or 405 response mean for an OCSP check?
It’s counted as Healthy. A plain GET isn’t a valid OCSP request, so responders often reject it with a 4xx code — that still proves the endpoint is listening and responding.
What does -DeepOCSPVerify actually test?
It runs certutil -verify -urlfetch against a copy of the CA certificate, performing a live fetch of the AIA, CDP, and OCSP data to confirm the full chain and revocation path resolve, not just that the endpoint answers.
Can I check a dedicated Online Responder separately from the CA?
Yes. Pass its name with -OCSPServers and the script checks the OCSPSvc service on that server directly. Without it, the script checks the CA servers themselves.
What happens if a CA is unreachable during a multi-server run?
The affected checks for that server degrade to a Warning finding rather than stopping the whole run, so you still get results for every other server you specified.
Can I run this across sites with firewalled segments?
If RPC, SMB, and HTTP are all blocked between sites, no script can reach across that boundary. Run the script locally on, or from a jumpbox within, each site instead.
Does the report still support search and filtering with multiple servers?
Yes. The search box and status filter buttons apply across all servers in the report, and a separate server dropdown narrows the view to one server at a time.
Where do I get the v1.3 script?
It’s available as a free download alongside the walkthrough on the original AD CS Health Check Script post, which now links to this v1.3 update.

Antonio Rennvick is an IT Infrastructure Manager with 15+ years running enterprise Active Directory, Microsoft 365, and Azure environments. He’s Microsoft certified (AZ-104, MS-102) and writes Core365 Cloud to share what actually works in production—PowerShell automation, AD deep dives, and security hardening drawn from real-world work, not test labs.

