Last Updated: October 2026
certutil -backup of the key, database and registry, and power off the old server. Then join the new server to the existing computer account, import the CA key, restore the database and registry, and publish fresh CRLs. A CA’s identity is its name and key pair, not its IP, so the only network work is DNS, firewall ports and CDP/AIA hosting.In our environment, the enterprise issuing CA ran on Windows Server 2012 R2 in one cloud network. It had to move to a Windows Server 2016 VM in a different network and keep the same computer name. Therefore the IP, the subnet and every firewall path changed, while the CA name, its key and its published URLs could not.
This guide is the runbook we used, plus the traps we hit. It covers the ports to open, pre-tests, the migration phases with a rollback at every stage, and post-migration tests. All names, domains and IPs are sanitized examples.
Table of contents
- How to migrate an issuing CA to a new server: the big picture
- Lab scenario and naming used in this guide
- Pre-migration checks before you migrate the issuing CA
- Which ports must be open on the new CA server?
- Pre-test the new server before cutover
- Step-by-step: migrate the issuing CA to the new server
- Rollback plan for every stage
- Post-migration tests: prove the new CA works
- Issues you may face and how to fix them
- Final checklist before you decommission the old CA
- Frequently Asked Questions
How to migrate an issuing CA to a new server: the big picture
An issuing CA is the subordinate certification authority that signs end-entity certificates for computers, users and services. An enterprise issuing CA also integrates with Active Directory, so its templates, enrollment object and CDP/AIA containers live in AD rather than on the server.
That split is what makes the same-name approach work. The AD objects already match the CA name, so they stay valid. As a result, you only move what lives on the server itself.
| Component | Where it lives | How it moves |
|---|---|---|
| CA private key and CA certificates | Machine store, in a key storage provider (KSP or CSP) | certutil -backup exports a password-protected .p12; import it on the new server |
| CA database (requests, issued and revoked certificates) | C:\Windows\System32\CertLog by default (.edb plus logs) | certutil -backup and certutil -restoreDB |
| CA configuration (CDP/AIA, CRL periods, flags) | HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration | reg export and reg import |
| Published certificate templates | Active Directory (Enrollment Services object) | Nothing to move; verify with certutil -catemplates |
| CA identity in AD | Enrollment Services, AIA and CDP containers | Nothing to move while the CA name stays the same |
| CRL and CA certificate files for HTTP | CertEnroll folder plus an IIS virtual directory | Copy files the new CA will not regenerate; recreate the virtual directory |
CAPolicy.inf | %windir% | Copy it if it exists |
| OCSP, NDES, web enrollment | Separate roles | Migrate or rebuild each one on its own |
Microsoft documents the same backup-and-restore principle in its AD CS migration guide. It also says that if you keep the role on the source server, you must disable the CA service and shut that server down before the destination comes up.
We take that second path on purpose. The old server stays intact but offline, which gives you a clean rollback for 30 days.
Lab scenario and naming used in this guide
Every name below is an example. The IP addresses come from the reserved documentation ranges 192.0.2.0/24 and 198.51.100.0/24, so none of them is a real address.
| Item | Example value | Notes |
|---|---|---|
| AD domain | core365.cloud | NetBIOS name CORE365 |
| Issuing CA host (old and new) | ISSCA01 (issca01.core365.cloud) | Same name on both servers |
| CA common name | core365-ISSCA01-CA | Enterprise subordinate CA |
| Old IP / new IP | 192.0.2.10 / 198.51.100.20 | Different networks |
| Offline root CA | Core365-RootCA on ROOTCA01 | Standalone, powered off |
| CDP/AIA alias | pki.core365.cloud | CNAME to issca01.core365.cloud |
| Domain controller | DC01.core365.cloud | Used for all AD checks |
| Test member server | TESTVM01 | Disposable VM for the issuance test |
| Old OS / new OS | Windows Server 2012 R2 / Windows Server 2016 Datacenter | Restore works to the same or a newer OS |
| Backup folder | C:\CABackup-Final | Holds the .p12, DataBase, .reg file and a CertEnroll copy |
Pre-migration checks before you migrate the issuing CA
Run these read-only checks on the old CA first. They decide whether a plain backup and restore will work. They also give you a baseline to compare against later.
Check the key provider and exportability
The key location decides your whole plan. A software key storage provider (KSP) or legacy CSP exports with certutil -backup. A hardware security module (HSM) needs the vendor’s procedure instead.
certutil -store My
certutil -getreg CA\CSP\Provider
certutil -getreg CA\CSP\HashAlgorithm
certutil -cainfo type
certutil -cainfoIn our case the provider was Microsoft Software Key Storage Provider. The store showed Private key is NOT plain text exportable, which is normal for CA keys. The backup still exported the key, and we proved it later with certutil -dump.
Also read CA cert count. Our CA reported 8 CA certificates (V0.0 to V7.0) that share one key, because it had been renewed several times with the same key. CA cert[0]: Unavailable is normal when the first certificate has expired. Note the thumbprint of the newest certificate, because you will select it on the new server.
The hash algorithm also travels with the registry. Ours was SHA1. Therefore treat a move to SHA256 as a separate change, not part of this migration.
certutil -backupKey fails with 0x80092004, the key is not exportable. You cannot move that key, so you would renew or re-key the CA instead.Record the database paths and size
A common mistake I see is querying the database paths under the CA subkey. They live one level up, directly under Configuration, so certutil -getreg CA\DBDirectory fails with ERROR_FILE_NOT_FOUND.
certutil -getreg DBDirectory
certutil -getreg DBLogDirectory
certutil -getreg DBSystemDirectory
(Get-ChildItem C:\Windows\System32\CertLog | Measure-Object Length -Sum).Sum / 1MBOur database folder was about 287 MB including logs, but the database file itself was only 35 MB. As a result, the backup and restore took seconds.
Review CRL and AIA publishing
certutil -getreg CA\CRLPublicationURLs
certutil -getreg CA\CACertPublicationURLs
certutil -getreg CA\CRLPeriod
certutil -getreg CA\CRLPeriodUnits
certutil -getreg CA\CRLDeltaPeriod
certutil -getreg CA\CRLDeltaPeriodUnits
certutil -getreg CA\CRLFlags| Flag | Name | Meaning |
|---|---|---|
| 1 | CSURL_SERVERPUBLISH | The CA writes the CRL to this location |
| 2 | CSURL_ADDTOCERTCDP | Add the URL to the CDP extension of issued certificates |
| 4 | CSURL_ADDTOFRESHESTCRL | Add the URL to the delta CRL (freshest CRL) extension |
| 8 | CSURL_ADDTOCRLCDP | Add the URL to the CRL itself |
| 64 | CSURL_SERVERPUBLISHDELTA | Publish delta CRLs to this location |
The question that matters is whether any URL uses the server name or IP directly. Ours used the alias pki.core365.cloud plus LDAP, so nothing was tied to the old IP. A CNAME like this keeps the URL stable when the host changes; our guide to DNS record types explains how aliases behave.
If a URL with the old IP is already embedded in issued certificates, you cannot fix that afterwards. Keep that IP reachable or plan reissuance.
Two numbers set your outage budget: the base CRL period (ours was 2 weeks) and the delta CRL period (1 day plus overlap). Therefore a long outage can let the delta CRL expire. Keep the window to a few hours. Also check CRLFlags. Ours included CRLF_REVCHECK_IGNORE_OFFLINE, which later explained why the CA could start despite an expired root CRL.
Find dependent roles and consumers
Get-WindowsFeature | Where-Object Installed | Select-Object Name
Get-SmbShare
Get-ScheduledTask | Where-Object { $_.TaskPath -notlike '\Microsoft\*' } | Select-Object TaskName, State
Resolve-DnsName pki.core365.cloudOur server also ran IIS for CDP/AIA over HTTP, plus CA Web Enrollment. Look for OCSP responders, NDES, scheduled tasks that copy CRLs, and firewall rules that reference the old IP. IIS is not part of the CA backup, so you rebuild it by hand.
Capture a baseline you can compare against
Record these before the change. After the migration, a pass means the numbers match or any difference is explained.
| Item | How to capture it | Why it matters |
|---|---|---|
| pkiview state | pkiview.msc screenshot | Shows pre-existing red items, so you do not blame the migration |
| Request count | (certutil -view -out RequestID | Select-String '^Row \d+:').Count | Proves the restored database matches |
| CA certificates and active thumbprint | certutil -cainfo | Expect the same count and active certificate |
| Published templates | certutil -catemplates | Expect the same list |
| CRL numbers and NextUpdate | certutil -dump on the CRL files | Confirms publishing continues |
| Cert Publishers membership | Get-ADGroupMember "Cert Publishers" | Confirms the computer account keeps its SID |
| Firewall rules for the old IP | Export from firewalls and security groups | You replicate them for the new IP |
Choose the target operating system
Restore works to the same or a newer Windows Server version. The database upgrades on first start, and that upgrade is one-way, so keep the old disks for rollback. In our case the target was Windows Server 2016. However, the end of extended support for Windows Server 2016 is January 12, 2027. If you are free to choose, pick a release with a longer support runway.
Which ports must be open on the new CA server?
Clients need TCP 135 and the RPC dynamic range for enrollment, plus TCP 80 for CRL and AIA downloads. The CA itself needs the usual domain member ports to reach domain controllers. Open them on the host firewall, on cloud network security groups and on every firewall between client sites and the new IP.
Inbound ports to the CA
| Port | Protocol | Purpose | Who needs it |
|---|---|---|---|
| 135 | TCP | RPC endpoint mapper for DCOM/RPC enrollment and remote CA management | Clients and admins |
| 49152-65535 | TCP | RPC dynamic ports where the CA service (certsvc) listens | Clients and admins, or your pinned range |
| 80 | TCP | CDP and AIA over HTTP: CRLs, delta CRLs and CA certificates | All clients, including non-domain devices |
| 443 | TCP | Only if you add HTTPS web enrollment, CES/CEP or NDES | Optional |
| 445 | TCP | SMB for admin shares and file copy | Admin subnets only |
| 3389 | TCP | RDP management | Admin subnets only |
| 5985 / 5986 | TCP | WinRM remoting | Optional, admins only |
| ICMP echo | ICMP | Diagnostics | Optional |
Outbound ports from the CA
| Port | Protocol | Purpose |
|---|---|---|
| 53 | TCP/UDP | DNS: resolve domain controllers and register the A record |
| 88 | TCP/UDP | Kerberos authentication |
| 135 and 49152-65535 | TCP | RPC to domain controllers (Netlogon, SAM, AD operations) |
| 389 | TCP/UDP | LDAP: read templates, publish CRLs and AIA to AD |
| 636 | TCP | LDAPS, if your domain enforces it |
| 3268 / 3269 | TCP | Global catalog (3269 for TLS) |
| 445 | TCP | SMB for Netlogon and SYSVOL |
| 464 | TCP/UDP | Kerberos password change (domain join and machine password) |
| 123 | UDP | NTP / Windows Time |
| 80 / 443 | TCP | Only if you publish CRLs to a separate web server |
Cloud and host firewall tips
- Set a static private IP in the cloud portal. Setting it only inside Windows leaves the NIC open to reassignment. Point the virtual network’s DNS servers at your domain DNS, and add the new subnet to AD Sites and Services.
- Inventory every rule that mentions the old IP. Security groups, NSGs, site firewalls and ACLs all need an equivalent for the new IP. In our migration this had the longest lead time.
- Pin the RPC range if you must. Windows uses TCP 49152-65535 by default. If your firewall team refuses that range, restrict RPC with the
HKLM\SOFTWARE\Microsoft\Rpc\Internetregistry values and reboot. It affects every RPC service on the host, so allow 100 ports or more. - Check the Windows Firewall rules on the CA. Run
Get-NetFirewallRule -Enabled True -Direction Inbound | Where-Object DisplayName -match 'World Wide Web|COM\+|RPC|Certificate'and confirm HTTP and DCOM/RPC are allowed.
To see which port the CA service actually uses, run this on the CA:
$svcPid = (Get-CimInstance Win32_Service -Filter "Name='certsvc'").ProcessId
Get-NetTCPConnection -State Listen -OwningProcess $svcPid | Select-Object LocalPortThen test from a client network. Test-NetConnection proves single ports only. The definitive test is certutil -ping, because it exercises the dynamic RPC port as well.
Test-NetConnection 198.51.100.20 -Port 135
Test-NetConnection 198.51.100.20 -Port 80
certutil -config "issca01.core365.cloud\core365-ISSCA01-CA" -pingPre-test the new server before cutover
Every test here has zero production impact. Run them days ahead, so the cutover window contains only the risky steps.
| # | Test | Command or action | Pass criteria |
|---|---|---|---|
| 1 | Reach a DC on AD ports | 53,88,135,389,445,464 | ForEach-Object { Test-NetConnection DC01.core365.cloud -Port $_ } | Every result shows TcpTestSucceeded : True |
| 2 | Resolve the domain and the alias | Resolve-DnsName core365.cloud and Resolve-DnsName pki.core365.cloud | DC addresses and the CNAME come back |
| 3 | Check time sync | w32tm /query /status | Small offset, domain hierarchy as the source |
| 4 | Client sites reach the new IP | Test-NetConnection 198.51.100.20 -Port 135 and -Port 80 from each site | True from every site |
| 5 | Install roles and IIS | Install-WindowsFeature ADCS-Cert-Authority, Web-Server, Web-Static-Content, Web-Dir-Browsing, Web-Filtering -IncludeManagementTools | Success, no reboot, default IIS page loads on port 80 |
| 6 | Rehearse the backup on the old CA | certutil -backup, then certutil -dump on the .p12 | Dump lists CA certificates with Encryption test passed |
| 7 | Rehearse the rollback tools | Stop and start the old VM; take a snapshot or image | You have console access and a restore point |
| 8 | Check your rights | Enterprise Admin (or delegated) plus local admin on both servers | You can reset the computer account and install an enterprise CA |
| 9 | Prepare DNS | Lower the A record TTL (for example to 300 seconds) and list the A, PTR and CNAME records | The new TTL is visible from a DC |
Clock accuracy matters twice: for Kerberos on the new VM and for CRL validity dates. Cloud VMs can drift against the domain hierarchy, so see our time drift troubleshooting guide if w32tm reports a large offset.
If you have a lab, you can also restore the backup into an isolated copy of your AD, with no route to production. It is the only way to rehearse the full restore without risk. However, most teams rely on a solid rollback plan instead.
Step-by-step: migrate the issuing CA to the new server
Run every command in an elevated PowerShell window. In our run, Install-AdcsCertificationAuthority, Select-String and Resolve-DnsName failed in a cmd window because they are PowerShell-only. That cost us time inside the change window.
Each phase ends with its rollback. The labels A, B and C match the diagram in Figure 1.
Phase 1: freeze issuance and take the final backup (old server)
Publish fresh CRLs first, so clients have maximum validity during the outage. Then block remote enrollment while you back up, so no certificate is issued after the backup.
New-Item -ItemType Directory C:\CABackup-Final -Force | Out-Null
certutil -crl
New-NetFirewallRule -DisplayName "CA-CUTOVER-FREEZE" -Direction Inbound -Action Block -Protocol TCP -LocalPort 135,49152-65535
certutil -ping # must still work locally
(certutil -view -out RequestID | Select-String '^Row \d+:').Count # row count A
certutil -backup C:\CABackup-Final # enter and record a strong password
reg export HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration C:\CABackup-Final\CAConfig.reg
robocopy C:\Windows\System32\CertSrv\CertEnroll C:\CABackup-Final\CertEnroll /E
Copy-Item C:\Windows\CAPolicy.inf C:\CABackup-Final\ -ErrorAction SilentlyContinue
(certutil -view -out RequestID | Select-String '^Row \d+:').Count # row count B, must equal A
certutil -dump C:\CABackup-Final\core365-ISSCA01-CA.p12 # prompts for the passwordBlock rules override allow rules in Windows Firewall, and local connections are normally unaffected. If your local -ping fails with the rule in place, delete the rule, run the backup quickly and stop the service straight away.
Row counts A and B must match. If they differ, a request slipped in, so take the backup again before moving on.
In the dump, every CA certificate should show its provider and Encryption test passed. The root certificate shows No key provider information, because its key is not on this server. That is expected.
Stop-Service certsvc
Set-Service certsvc -StartupType Disabled
icacls C:\CABackup-Final /inheritance:r /grant:r "Administrators:(OI)(CI)F" "SYSTEM:(OI)(CI)F"Copy C:\CABackup-Final to two locations away from this server. It contains the CA private key, so protect it and store the password in a vault.
Set-Service certsvc -StartupType Automatic; Start-Service certsvc; Remove-NetFirewallRule -DisplayName CA-CUTOVER-FREEZEPhase 2: take the old server offline
Stop the VM, but do not delete it. Then snapshot or image its disks. A snapshot is point-in-time as soon as it starts, so you can restart the VM while it completes. Keep both for at least 30 days.
While the old server is off, the HTTP CDP and AIA are unavailable until Phase 5. Domain clients fall back to LDAP and cached CRLs stay valid, so keep Phases 3 to 5 tight.
Phase 3: join the new server to the existing computer account
Reuse the old computer account instead of deleting it. The SID stays the same, so Cert Publishers membership, DNS record ownership and other permissions carry over.
- On a DC, record the current state:
Get-ADComputer ISSCA01 -Properties MemberOf | Select-Object Name, SID, MemberOf - In Active Directory Users and Computers, right-click
ISSCA01and choose Reset Account. Do this immediately before the join, because it breaks the old server’s trust (Figure 4). - On the new server, still in a workgroup, rename it and join the domain (commands below).
- After the reboot, verify the secure channel and DNS, and force replication.
Rename-Computer -NewName ISSCA01 -Restart
# after the reboot, still in the workgroup
Add-Computer -DomainName core365.cloud -Credential (Get-Credential) -Restart
# after the second reboot
nltest /sc_verify:core365.cloud
Resolve-DnsName issca01.core365.cloud # should now return the new IP
Get-ADGroupMember "Cert Publishers" | Select-Object Name # run on a DC
repadmin /syncall /AdeP # run on a DCThe join also updates the A record through secure dynamic DNS. As a result, clients start resolving the new IP immediately, before the CA is restored. If a stale record survives, run ipconfig /registerdns on the new server, and watch replication with our AD replication dashboard.
Phase 4: restore the CA key, database and registry (new server)
Import the PFX first. Then confirm you can see the newest CA certificate and that the root is trusted.
certutil -f -csp "Microsoft Software Key Storage Provider" -importpfx C:\CABackup-Final\core365-ISSCA01-CA.p12
certutil -store My <thumbprint-of-newest-CA-cert>
certutil -store Root | findstr /i Core365-RootCANext, configure the CA with the existing certificate. Select the newest certificate by thumbprint, because all of them share one key and the wizard can list several.
Install-AdcsCertificationAuthority -CAType EnterpriseSubordinateCA -CertificateID <thumbprint-of-newest-CA-cert> -ForceYou can use Server Manager instead: AD CS Configuration, then Use existing certificate and private key, then Import. If the cmdlet rejects the parameter combination, use the wizard.
The first start can fail with event 100 (CRYPT_E_REVOCATION_OFFLINE). In our run it did, before the registry import, because a default configuration does not tolerate an offline root CRL. The restored registry brought back the old CRLFlags with ignore-offline set, and then the CA started.
Stop-Service certsvc
reg export HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration C:\NewCA-AfterInstall.reg # undo point
certutil -f -restoreDB C:\CABackup-Final
reg import C:\CABackup-Final\CAConfig.reg
Start-Service certsvcThe service must be stopped for -restoreDB, and the command reminds you to restart it to finish the restore. Because we moved to a newer OS, the first start upgraded the database (event 124). That upgrade is one-way.
Now verify the restore:
certutil -cainfo | Select-String 'CA name|CA cert count|CA cert\[7\]|CRL\[0\]'
certutil -ping
certutil -catemplates
(certutil -view -out RequestID | Select-String '^Row \d+:').Count # compare with row count A
Get-ChildItem C:\Windows\System32\CertLog | Select-Object Name, Length, LastWriteTime
certutil -crlIn our case CA cert count: 8 came back, CA cert[0] was Unavailable as before, and the .edb matched the backup size to the byte. Use the row-count comparison as the second proof. Do not rely on one signal alone.
Phase 5: recreate CRL and AIA hosting (new server)
The CA role does not create the IIS virtual directory by itself. Recreate it, then copy only the files the new CA has not generated, such as the offline root CA’s CRL and certificate.
Import-Module WebAdministration
New-WebVirtualDirectory -Site "Default Web Site" -Name CertEnroll -PhysicalPath "C:\Windows\System32\CertSrv\CertEnroll"
robocopy C:\CABackup-Final\CertEnroll C:\Windows\System32\CertSrv\CertEnroll /E /XC /XN /XO /XF nsrev_*.asp
Invoke-WebRequest "http://pki.core365.cloud/CertEnroll/" -UseBasicParsing | Select-Object StatusCodeThe /XC /XN /XO switches copy only files that do not exist yet, so robocopy never overwrites a CRL the new CA just published. The old web.config enables directory browsing, so install the Directory Browsing feature first. Otherwise IIS returns a 500.19 error for the folder.
Skip web enrollment unless someone needs it. Web enrollment over HTTP with Windows authentication is a known NTLM relay target (ESC8), so reinstall it only on demand, and then use HTTPS with Extended Protection.
Phase 6: let clients in and validate
Nothing needs to be reopened, because the freeze rule lived on the old server, which is now off. Move straight to the post-migration tests below, and stop at the first failure.
Rollback plan for every stage
The old server stays intact until you decommission it, so every stage has a way back. The table maps each stage to its rollback label from Figure 1.
| Stage | Rollback steps | Notes |
|---|---|---|
| A: after the freeze or after stopping the old CA | Set-Service certsvc -StartupType Automatic, then Start-Service certsvc, then Remove-NetFirewallRule -DisplayName CA-CUTOVER-FREEZE | Nothing else has changed |
| B: old VM stopped, new server not yet joined | Start the old VM, then do A | DNS and AD are unchanged |
| C: new server joined or CA restored | 1. Stop or deallocate the new VM. 2. Start the old VM and sign in with a local admin. 3. Test-ComputerSecureChannel -Repair -Credential (Get-Credential). 4. Do A. 5. ipconfig /registerdns and confirm the A record shows the old IP. 6. certutil -ping, certutil -crl, then compare pkiview. | Certificates the new CA issued exist only in its database |
| Point of no return | Wiping the old VM and its image | Wait at least 30 days |
Resetting the computer account breaks the old server’s AD trust, even if the join never finished. That is why step 3 of Rollback C exists. If you meet the classic error message, see our guide to the Active Directory trust relationship failed error.
Before you roll back after clients enrolled, list what the new CA issued. Those certificates stay valid but are unknown to the old CA, so you must reissue them or accept that the old CA cannot revoke them.
certutil -view -restrict "RequestID>=<first-new-request-id>" -out RequestID,CommonNamePost-migration tests: prove the new CA works
Run these in order. Stop at the first failure, because later tests depend on earlier ones.
| # | Test | Command or action | Expected result |
|---|---|---|---|
| 1 | Service and database | Get-Service certsvc; compare the .edb size and row count with the backup | Running; sizes and counts match |
| 2 | Event log | Get-WinEvent -FilterHashtable @{LogName='Application'; ProviderName='Microsoft-Windows-CertificationAuthority'} -MaxEvents 15 | Event 26 (started), event 124 after an OS upgrade, no unexplained errors |
| 3 | CA identity | certutil -cainfo and certutil -catemplates | Same name, certificate count, active certificate and templates as the baseline |
| 4 | DNS | Resolve-DnsName issca01.core365.cloud and pki.core365.cloud from a DC and from each site | The new IP everywhere |
| 5 | RPC from other networks | certutil -config "issca01.core365.cloud\core365-ISSCA01-CA" -ping from a client in each site | Interface is alive |
| 6 | HTTP CDP and AIA | Invoke-WebRequest http://pki.core365.cloud/CertEnroll/ -UseBasicParsing | Status 200 |
| 7 | Enrollment | Get-Certificate -Template Machine -CertStoreLocation Cert:\LocalMachine\My on a disposable VM | Status Issued |
| 8 | Chain and revocation | Export the certificate, then certutil -verify -urlfetch test.cer | Leaf revocation check passed through LDAP |
| 9 | Revocation | certutil -revoke <serial> 5, certutil -crl, then verify again | The serial appears in the CRL and verify reports it revoked |
| 10 | pkiview | pkiview.msc | Matches the baseline from Figure 2 |
| 11 | Backup | certutil -backup of the new CA | Copied off the server and scheduled |
Use a disposable VM for test 7. A common mistake I see is testing on a production member server. The Machine template then issues that server’s real certificate, and revoking it leaves a revoked certificate in its store. Delete the test certificate afterwards, and run certutil -pulse if the server must re-enroll.
In our test, the leaf certificate’s revocation check passed through LDAP. The HTTP base CRL URL returned a delta CRL, and the + delta URL returned 404. Both matched the old server’s baseline, and the next section explains them. For a broader view afterwards, run the AD CS health check script.
Keep the old VM and its image for 30 days, and decommission only after at least one full base CRL cycle. Meanwhile, put a calendar reminder on the first scheduled backup of the new CA.
Issues you may face and how to fix them
These are the problems we hit or verified. The table is the quick lookup. The sections after it cover the faults that need more than one line.
| Symptom or error | Likely cause | Fix |
|---|---|---|
certutil -getreg CA\DBDirectory returns ERROR_FILE_NOT_FOUND | DB paths are not under the CA subkey | Use certutil -getreg DBDirectory |
certutil -backupKey fails with 0x80092004 | The key is not exportable (HSM or non-exportable key) | Use the vendor procedure, or renew or re-key the CA |
Install-AdcsCertificationAuthority or Select-String is not recognized | You are in cmd | Use an elevated PowerShell window |
CA will not start; events 100 and 48; 0x80092013 | Revocation check of the CA chain is offline (expired root CRL) | Restore CRLFlags or refresh the root CRL (see below) |
Events 86 and 87; NTE_BAD_KEYSET | CA exchange key was not found, so the CA creates a new one | Benign if certutil -cainfo xchg returns a certificate (see below) |
certutil -catemplates shows Auto-Enroll: Access is denied | Your account cannot read template ACLs | Cosmetic; the template list is still correct |
-restoreDB fails or the service will not start after restore | Service was running, or %SystemRoot% and DB paths differ from the backup | Stop certsvc first and align the DB paths in the registry |
| Domain join reports that the account already exists | The old account was not reset, or you lack rights | Reset the account in ADUC and use credentials that can reuse it |
| CRL publishing to AD fails with access denied | The computer account was recreated, so its SID changed and it left Cert Publishers | Add it to Cert Publishers, or reuse the old account |
| Clients still reach the old IP | DNS cache, high TTL, stale record or replication delay | Lower the TTL beforehand; ipconfig /flushdns; repadmin /syncall /AdeP |
The RPC server is unavailable (0x800706BA) from some sites | The dynamic RPC range is blocked on a firewall or NSG | Open or pin the range, then test certutil -ping per site |
pkiview: HTTP CDP and delta show Unable to download | The delta CRL overwrote the base file; IIS blocks + (404.11) | Fix the publish path and allowDoubleEscaping (see below) |
pkiview: root CDP shows Expired | The offline root CRL lapsed | Refresh the root CRL (see below) |
IIS returns 500.19 for /CertEnroll | web.config enables directory browsing but the feature is missing | Install Web-Dir-Browsing or remove the web.config |
| Old server reports a failed trust after rollback | The join reset its machine password | Test-ComputerSecureChannel -Repair |
CDP shows a literal <ServerName> in an LDAP URL | A placeholder baked into the issuing CA certificate | Cosmetic while another CDP works; only a renewed CA certificate clears it |
CA will not start: CRYPT_E_REVOCATION_OFFLINE (events 100 and 48)
At startup, the CA validates its own certificate chain. If the offline root CRL has expired or its URL is unreachable, the check fails with 0x80092013 and the service stops. In our run this happened on the first start, before the registry import.
The old CA had CRLFlags = 10, which includes CRLF_REVCHECK_IGNORE_OFFLINE (8). The import restored it, and the CA then started. Treat that flag as a crutch, not a fix.
certutil -getreg CA\CRLFlags
certutil -setreg CA\CRLFlags +CRLF_REVCHECK_IGNORE_OFFLINE
Restart-Service certsvcEvents 86 and 87 (NTE_BAD_KEYSET) after the restore
The CA logged that it could not use the provider for encryption keys. These events relate to the CA exchange certificate, a short-lived certificate (about a week) used for key archival. Its key container does not exist on a new server, so the CA creates a new one.
certutil -cainfo xchg
certutil -getreg CA\EncryptionCSP\*In our case certutil -cainfo xchg returned a freshly generated -Xchg certificate created in the same minute as the events. The EncryptionCSP values also matched the old registry. Therefore we treated the events as benign and rechecked the log at the weekly renewal. If you use key archival with a configured KRA, investigate further instead of assuming.
HTTP CRL problems that migrate with you
Two faults travel with the registry. First, a local publish path without %9 makes the daily delta CRL overwrite the base CRL file. Second, IIS blocks the + character in the delta CRL file name.
You can recognize it in three places. certutil -dump on the base .crl shows a Delta CRL Indicator. certutil -verify -urlfetch expects a base CRL but receives a delta. And the + URL returns 404.11.
certutil -setreg CA\CRLPublicationURLs "65:C:\Windows\system32\CertSrv\CertEnroll\%3%8%9.crl\n6:http://pki.core365.cloud/CertEnroll/%3%8%9.crl\n79:ldap:///CN=%7%8,CN=%2,CN=CDP,CN=Public Key Services,CN=Services,%6%10"
Restart-Service certsvc
certutil -crl
& "$env:windir\system32\inetsrv\appcmd.exe" set config "Default Web Site/CertEnroll" -section:system.webServer/security/requestFiltering -allowDoubleEscaping:true -commit:apphostDo this as a separate change after the migration is validated, so your pkiview baseline stays comparable during cutover. Issued certificates already carry the correct %9 URL, so only the published files change.
The offline root CRL has expired
pkiview builds the root’s AIA and CDP entries from the subordinate CA certificates it issued. Therefore the URLs are baked in, and you can only fix what exists at those locations. Power on the offline root, check its clock first, set a validity you can maintain, and publish.
certutil -getreg CA\CRLPeriod
certutil -getreg CA\CRLPeriodUnits
certutil -setreg CA\CRLPeriodUnits 12
certutil -setreg CA\CRLPeriod "Months"
Restart-Service certsvc
certutil -crl
Get-ChildItem C:\Windows\System32\CertSrv\CertEnroll\*.crl | Select-Object Name, LastWriteTimeCopy the new .crl files to the issuing CA’s CertEnroll folder with the exact file names from the CDP URL, for example Core365-RootCA(1).crl. Then verify the dates, clear the URL cache and refresh pkiview.
certutil -dump "C:\Windows\System32\CertSrv\CertEnroll\Core365-RootCA(1).crl" | Select-String 'ThisUpdate|NextUpdate'
certutil -urlcache * deleteTo fix the AIA entry in AD, publish the root certificate from a DC with certutil -dspublish -f <root-cert>.cer RootCA. An LDAP CDP that shows a literal <ServerName> stays red. Clients ignore it as long as the HTTP CDP works, and only a renewed issuing CA certificate clears it.
Final checklist before you decommission the old CA
- The new CA runs, row counts match and the event log is clean.
- A test certificate was issued, revoked and published, and pkiview matches the baseline.
- Every client network passes
certutil -pingand loads the HTTP CDP. - A fresh backup of the new CA sits off the server and runs on a schedule.
- The DNS TTL is back to its normal value.
- The old VM and its image are kept for 30 days, with a calendar entry to review them.
- Follow-up changes are booked: the root CRL, the HTTP CDP fixes, SHA256 and the OS lifecycle.
A same-name, new-IP migration is mostly discipline: freeze, back up, prove the backup, and keep the old server for rollback. If you do those four things and test from every network, the CA move itself is the easy part.
Frequently Asked Questions
Can you migrate an issuing CA to a new server with a different IP address?
Yes. A CA’s identity is its name and key pair, not its IP. Keep the same computer name, restore the key, database and registry, then update DNS and firewall rules for the new IP. Clients keep trusting the CA because the CA certificate and the CDP/AIA URLs do not change.
Do I need to reissue certificates after migrating an issuing CA?
No. Certificates already issued stay valid because the CA key and name are unchanged and the database is restored. Only certificates issued between your backup and shutdown would be lost, which is why you freeze issuance and compare row counts before stopping the old CA.
Which ports must be open on the new CA server?
Clients need TCP 135 and the RPC dynamic range (49152-65535 by default) for enrollment, plus TCP 80 for CRL and AIA downloads. The CA needs DNS, Kerberos, LDAP, RPC, SMB and NTP access to domain controllers. Administrators also need RDP or WinRM from admin subnets.
Can the CA private key be backed up if it is not exportable?
Usually yes on a software key storage provider. certutil -backup exported our key even though certutil reported it as not plain text exportable. If certutil -backupKey fails with 0x80092004, the key cannot be exported. HSM keys follow the vendor’s procedure. Verify any backup with certutil -dump.
How long should the CA be offline during the migration?
Plan 2 to 3 hours and keep the outage far below the delta CRL validity, which is often one day plus overlap. Publish fresh CRLs right before shutdown. Domain clients fall back to LDAP and cached CRLs, so most users never notice a short outage.
Can I change the CA name or computer name during migration?
The CA common name cannot change. The computer name can change only if your CDP and AIA URLs use a DNS alias instead of the host name. Microsoft’s default CDP extensions include the host name, which is why keeping the same name is the simplest path.
Are certificate templates restored automatically?
Yes. Published templates are stored in Active Directory on the CA’s Enrollment Services object, so they reappear once the CA with the same name is running. Verify with certutil -catemplates. Access denied messages next to Auto-Enroll only mean the account cannot read template permissions.
Why does the CA fail to start after I import the key?
The usual cause is CRYPT_E_REVOCATION_OFFLINE (0x80092013, event 100). The CA checks revocation for its own chain, and an expired or unreachable root CRL makes that check fail. Refresh the offline root CRL, or restore the registry CRLFlags value that ignores offline revocation, then restart the service.
Should I uninstall AD CS from the old server first?
Not if you want a rollback path. Microsoft’s guidance allows either removing the role or disabling the CA service and shutting the old server down before the new one takes the name. Disabling and powering off keeps the old CA intact for 30 days.
How do I confirm the database restored correctly?
Compare the request count before and after with certutil -view -out RequestID, check that the .edb size matches the backup, and confirm event 26 in the Application log. After an OS upgrade you should also see event 124. Matching counts plus a clean start mean the restore is intact.
Do I need to copy CRL and AIA files to the new server?
Yes, but only files the new CA will not regenerate, mainly the offline root CA’s CRL and certificate. The new CA publishes its own CRLs and CA certificates. Recreate the IIS virtual directory and copy missing files with robocopy switches that never overwrite newer files.
What happens to OCSP, NDES and web enrollment?
They are separate roles and do not move with certutil -backup. Migrate or rebuild each one, then point it at the CA. Web enrollment over HTTP with Windows authentication is an NTLM relay risk (ESC8), so reinstall it only if users need it and protect it with HTTPS.
How do I roll back if the new CA fails validation?
Shut down the new server, start the old one, repair its AD trust with Test-ComputerSecureChannel -Repair, restore the DNS record, and re-enable the CA service. Certificates the new CA issued exist only in its database, so list them before rolling back.

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.


