PKI: which templates are published where?

Windows Server has two kinds of Certificate Authorities: Standalone and Enterprise. This strangely named difference really only means one thing: an Enterprise CA can (must) use templates for certificates it issues. Using templates you enforce standards for your private certificates, and enable desirable features like autoenrollment.

A template exists as an object in the Configuration Container of the forest. By itself, it does nothing. It first needs to be published by an Enterprise CA, at which point it can be used for enrollment. As a best practice, you should never use built-in templates, but always duplicate them first and give them a new name with a suitable naming convention.

After a while, you may lose track of what templates are issued where, certainly if you have multiple CAs. This can become a problem if you intend CAs to be each other's backup: you must be sure that templates are published on both CAs. For a customer I was troubleshooting enrollment and needed a quick overview. So, I put together a script to make a list of all Enterprise CAs and the templates that they publish. Simply execute this code in Powershell 3.0 or higher to get a list of all published templates and their CAs. No output? Then you have no Enterprise CAs.

[powershell]
$pkiContainerDN = "CN=Public Key Services,CN=Services,$((Get-ADRootDSE).configurationNamingContext)"
$enrollmentServers = Get-ADObject -filter { objectclass -eq "pkiEnrollmentService" } -SearchBase $pkiContainerDN -Properties * | Sort-Object dnsHostName
$publishedTemplates = $enrollmentServers | ForEach-Object { $_.certificateTemplates } | Sort-Object -Unique

$publishedTemplates | ForEach-Object {
$obj = [PSCustomObject] @{
template = $_
}
foreach ($CA in $enrollmentServers)
{
$obj | add-member Noteproperty $CA.dnsHostName -Value ($CA.certificatetemplates -contains $obj.template)
}
$obj
} | out-gridview
[/powershell]

The script discovers the configuration context, determines the PKI container, queries for all enterprise CAs ( $enrollmentServers), and from those, gets the published templates. All unique templates are put together in the array $publishedTemplates, which gets pushed into a pipeline for output. Then, I put all data in a [PSCustomObject] for easier further processing. In this case, I choose to simply dump it into the out-gridview. Should this last bit fail, remember that out-gridview requires Powershell ISE to be installed. To run the script you need no special permissions.

The following is an illustration from of my labs:

published-where

I have three Enterprise CAs. One is publishing no templates at all, and the other two are active. In fact, my configuration is a mistake. I intended for ca2-issuing1 to be a backup for ca3-issuing2, but I forgot to enable publishing. A final note: the templates are listed by their real names, not their display names. For a quick job like this I did not bother to make that translation.