Private/Publish-CertificateTemplate.ps1
Function Publish-CertificateTemplate { <# .SYNOPSIS Publishes a certificate template to all available Certification Authorities (CAs). .DESCRIPTION This function publishes a specified certificate template to all Enterprise Certification Authorities in the Active Directory forest. It performs the following: - Discovers writable Domain Controllers - Locates all Enterprise CAs - Publishes the template to each CA - Validates the publication success .PARAMETER CertDisplayName Specifies the display name of the certificate template to be published. Spaces will be removed from the name during processing. .PARAMETER Server Optional. FQDN of the Domain Controller to use. If not specified, discovers nearest writable DC. .INPUTS System.String You can pipe the certificate template display name to this function. .OUTPUTS [System.Void] This function does not produce any output. .EXAMPLE Publish-CertificateTemplate -CertDisplayName "Web Server Template" Publishes the template to all CAs using default credentials. .EXAMPLE "User Auth" | Publish-CertificateTemplate -Verbose Pipes a template name to the function and publishes it with verbose output. .NOTES Used Functions: Name ║ Module/Namespace ═══════════════════════════════════════╬══════════════════════════════ Get-ADDomainController ║ ActiveDirectory Get-ADObject ║ ActiveDirectory Set-ADObject ║ ActiveDirectory Write-Verbose ║ Microsoft.PowerShell.Utility Write-Debug ║ Microsoft.PowerShell.Utility Write-Error ║ Microsoft.PowerShell.Utility Write-Progress ║ Microsoft.PowerShell.Utility Get-FunctionDisplay ║ EguibarIT .NOTES Version: 1.2 DateModified: 22/May/2025 LastModifiedBy: Vicente Rodriguez Eguibar vicente@eguibar.com Eguibar IT http://www.eguibarit.com .LINK https://github.com/vreguibar/EguibarIT/blob/main/Private/Publish-CertificateTemplate.ps1 .COMPONENT PKI .ROLE Certificate Management .FUNCTIONALITY Certificate Template Publication #> [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'Medium' )] [OutputType([void])] Param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'Display name of the certificate template to publish')] [ValidateNotNullOrEmpty()] [string] $CertDisplayName, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [Alias('DomainController', 'DC')] [string] $Server ) begin { Set-StrictMode -Version Latest # Output header information if ($null -ne $Variables -and $null -ne $Variables.Header) { $txt = ($Variables.Header -f (Get-Date).ToString('dd/MMM/yyyy'), $MyInvocation.Mycommand, (Get-FunctionDisplay -HashTable $PsBoundParameters -Verbose:$False) ) Write-Verbose -Message $txt } #end If ###################### # Initialize variables [hashtable]$Splat = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) [hashtable]$CommonParams = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) [hashtable]$GetDCParams = [hashtable]::New([StringComparer]::OrdinalIgnoreCase) try { # Prepare common parameters $CommonParams = @{ ErrorAction = 'Stop' } # Get DC if not specified if (-not $PSBoundParameters.ContainsKey('Server')) { $GetDCParams = @{ Discover = $true ForceDiscover = $true Writable = $true } $GetDCParams += $CommonParams $Server = (Get-ADDomainController @GetDCParams).HostName[0] Write-Debug -Message ('Using Domain Controller: {0}' -f $Server) } #end If $CommonParams['Server'] = $PSBoundParameters['Server'] # Get enrollment path $EnrollmentPath = 'CN=Enrollment Services,CN=Public Key Services,CN=Services,{0}' -f $Variables.configurationNamingContext Write-Debug -Message ('Enrollment path: {0}' -f $EnrollmentPath) # Get all CAs $Splat = @{ SearchBase = $EnrollmentPath SearchScope = 'OneLevel' Filter = '*' } $CAs = Get-ADObject @Splat @CommonParams if (-not $CAs) { throw 'No Certificate Authorities found in the forest' } #end If Write-Verbose -Message ('Found {0} Certificate Authorities' -f $CAs.Count) } catch { Write-Error -Message ('Failed to initialize: {0}' -f $_.Exception.Message) throw } #end Try-Catch } #end Begin process { # Remove spaces from template name $TemplateToAdd = $CertDisplayName.Replace(' ', '') Write-Debug -Message ('Processing template: {0}' -f $TemplateToAdd) $processedCAs = 0 foreach ($CA in $CAs) { $processedCAs++ Write-Progress -Activity 'Publishing Certificate Template' -Status $CA.Name ` -PercentComplete (($processedCAs / $CAs.Count) * 100) try { $CAIdentity = $CA.DistinguishedName Write-Debug -Message ('Processing CA: {0}' -f $CAIdentity) if ($PSCmdlet.ShouldProcess( "Certificate Template: $TemplateToAdd", "Publish to CA: $($CA.Name)")) { $Splat = @{ Identity = $CAIdentity Add = @{certificateTemplates = $TemplateToAdd } } Set-ADObject @Splat @CommonParams Write-Verbose -Message ('Template published to CA: {0}' -f $CA.Name) } } catch [Microsoft.ActiveDirectory.Management.ADServerDownException] { Write-Error -Message ('CA server unavailable: {0}' -f $CA.Name) continue } catch { Write-Error -Message (' Failed to publish template to CA {0}: {1}' -f $CA.Name, $_.Exception.Message ) continue } } #end foreach Write-Progress -Activity 'Publishing Certificate Template' -Completed } #end Process end { if ($null -ne $Variables -and $null -ne $Variables.Footer) { $txt = ($Variables.Footer -f $MyInvocation.InvocationName, 'publishing Cert Template (Private Function).' ) Write-Verbose -Message $txt } #end If } #end End } #end Function Publish-CertificateTemplate |