Private/Enable-Template.ps1

function Enable-Template {
    <#
        .SYNOPSIS
        Enables certificate templates for enrollment by adding them to Certificate Authority configurations.
 
        .DESCRIPTION
        This function takes one or more certificate template names and enables them for enrollment
        by adding their names to the certificateTemplates attribute on all Certificate Authorities
        in the current forest. This allows clients to request certificates based on these templates.
 
        The function uses DirectoryEntry objects to modify the CA configurations without requiring
        the ActiveDirectory PowerShell module.
 
        .PARAMETER Template
        One or more certificate template DirectoryEntry objects to enable for enrollment.
        These should be DirectoryEntry objects representing pKICertificateTemplate objects
        from Active Directory.
 
        .PARAMETER CertificateAuthority
        Optional. Specific Certificate Authority objects to modify. If not provided, the function
        will discover and modify all CAs in the current forest.
 
        .PARAMETER WhatIf
        Shows what changes would be made without actually performing them.
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        Certificate template DirectoryEntry objects to enable.
 
        .OUTPUTS
        PSCustomObject[]
        Returns result objects indicating success/failure for each CA modification.
 
        .EXAMPLE
        $Templates = Get-AdcsObjects | Where-Object { $_.ObjectClass -eq 'pKICertificateTemplate' }
        $UserTemplate = $Templates | Where-Object { $_.Properties['name'].Value -eq 'User' }
        Enable-Template -Template $UserTemplate
 
        .EXAMPLE
        $Templates = Get-AdcsObjects | Where-Object { $_.ObjectClass -eq 'pKICertificateTemplate' }
        $Templates | Enable-Template -WhatIf
 
        .EXAMPLE
        $CAs = Get-AdcsObjects | Where-Object { $_.ObjectClass -eq 'pKIEnrollmentService' }
        $Template = Get-AdcsObjects | Where-Object { $_.Properties['name'].Value -eq 'WebServer' }
        Enable-Template -Template $Template -CertificateAuthority $CAs
 
        .LINK
        https://docs.microsoft.com/en-us/windows-server/networking/core-network-guide/cncg/server-certs/configure-the-server-certificate-template
 
        .NOTES
        Requires appropriate permissions to modify Certificate Authority objects in Active Directory.
        The function will skip templates that are already enabled on each CA.
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [ValidateNotNull()]
        [System.DirectoryServices.DirectoryEntry[]]$Template,
        
        [Parameter()]
        [System.DirectoryServices.DirectoryEntry[]]$CertificateAuthority,
        
        [Parameter()]
        [switch]$PassThru
    )


    begin {
        Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Starting $($MyInvocation.MyCommand) on $env:COMPUTERNAME..."
        
        # Initialize results array
        $results = @()
        
        # If no specific CAs provided, discover all CAs in the forest
        if (-not $CertificateAuthority) {
            Write-Verbose "No specific CAs provided, discovering all CAs in forest..."
            
            try {
                # Get the current forest
                $forest = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest()
                Write-Verbose "Forest: $($forest.Name)"
                
                # Search for Certificate Authorities (pKIEnrollmentService objects)
                $configContext = "CN=Configuration," + $forest.RootDomain.GetDirectoryEntry().Properties['distinguishedName'].Value
                $searcher = [System.DirectoryServices.DirectorySearcher]::new()
                $searcher.SearchRoot = [System.DirectoryServices.DirectoryEntry]::new("LDAP://$configContext")
                $searcher.Filter = "(objectClass=pKIEnrollmentService)"
                $searcher.SearchScope = [System.DirectoryServices.SearchScope]::Subtree
                
                $caResults = $searcher.FindAll()
                $CertificateAuthority = @()
                
                foreach ($result in $caResults) {
                    $CertificateAuthority += $result.GetDirectoryEntry()
                }
                
                Write-Verbose "Found $($CertificateAuthority.Count) Certificate Authorities"
                
                # Clean up
                $searcher.Dispose()
                $caResults.Dispose()
                
            } catch {
                Write-Error "Failed to discover Certificate Authorities: $($_.Exception.Message)"
                return
            }
        }
        
        if ($CertificateAuthority.Count -eq 0) {
            Write-Warning "No Certificate Authorities found or provided"
            return
        }
    }

    process {
        foreach ($templateObj in $Template) {
            # Validate that this is a certificate template
            if ($templateObj.SchemaClassName -ne 'pKICertificateTemplate') {
                Write-Warning "Object is not a certificate template (SchemaClassName: $($templateObj.SchemaClassName))"
                continue
            }
            
            # Get the template name for enrollment
            $templateName = $templateObj.Properties['name'].Value
            Write-Verbose "Processing template: $templateName"
            
            foreach ($ca in $CertificateAuthority) {
                try {
                    # Refresh the CA object to get current values
                    $ca.RefreshCache()
                    
                    $caName = $ca.Properties['name'].Value
                    $caDN = $ca.Properties['distinguishedName'].Value
                    
                    Write-Verbose "Processing CA: $caName"
                    Write-Verbose "CA DN: $caDN"
                    
                    # Get current certificate templates
                    $currentTemplates = @()
                    if ($ca.Properties['certificateTemplates'].Count -gt 0) {
                        $currentTemplates = @($ca.Properties['certificateTemplates'].Value)
                    }
                    
                    Write-Verbose "Current templates on $caName : $($currentTemplates -join ', ')"
                    
                    # Check if template is already enabled
                    if ($templateName -in $currentTemplates) {
                        Write-Verbose "Template '$templateName' is already enabled on CA '$caName'"
                        
                        $results += [PSCustomObject]@{
                            Success = $true
                            CertificateAuthority = $caName
                            CertificateAuthorityDN = $caDN
                            TemplateName = $templateName
                            TemplateDistinguishedName = $templateObj.Properties['distinguishedName'].Value
                            Action = "Already Enabled"
                            Error = $null
                        }
                        
                        continue
                    }
                    
                    # Add the template to the certificateTemplates attribute
                    if ($PSCmdlet.ShouldProcess("$caName", "Enable template '$templateName'")) {
                        # Add the new template to the list
                        $ca.Properties['certificateTemplates'].Add($templateName)
                        
                        # Commit the changes
                        $ca.CommitChanges()
                        
                        Write-Verbose "Successfully enabled template '$templateName' on CA '$caName'"
                        
                        $results += [PSCustomObject]@{
                            Success = $true
                            CertificateAuthority = $caName
                            CertificateAuthorityDN = $caDN
                            TemplateName = $templateName
                            TemplateDistinguishedName = $templateObj.Properties['distinguishedName'].Value
                            Action = "Enabled"
                            Error = $null
                        }
                    } else {
                        # WhatIf scenario
                        $results += [PSCustomObject]@{
                            Success = $true
                            CertificateAuthority = $caName
                            CertificateAuthorityDN = $caDN
                            TemplateName = $templateName
                            TemplateDistinguishedName = $templateObj.Properties['distinguishedName'].Value
                            Action = "Would Enable"
                            Error = $null
                        }
                    }
                    
                } catch {
                    $errorMsg = "Failed to enable template '$templateName' on CA '$caName': $($_.Exception.Message)"
                    Write-Warning $errorMsg
                    
                    $results += [PSCustomObject]@{
                        Success = $false
                        CertificateAuthority = $caName
                        CertificateAuthorityDN = $caDN
                        TemplateName = $templateName
                        TemplateDistinguishedName = $templateObj.Properties['distinguishedName'].Value
                        Action = "Failed"
                        Error = $errorMsg
                    }
                }
            }
        }
    }

    end {
        Write-Verbose "[$(Get-Date -Format 'yyyy-MM-dd hh:mm:ss')] Finishing $($MyInvocation.MyCommand) on $env:COMPUTERNAME..."
        
        if ($PassThru -or $WhatIfPreference) {
            Write-Output $results
        }
        
        # Summary
        $successful = $results | Where-Object { $_.Success -and $_.Action -eq "Enabled" }
        $alreadyEnabled = $results | Where-Object { $_.Success -and $_.Action -eq "Already Enabled" }
        $failed = $results | Where-Object { -not $_.Success }
        
        Write-Verbose "Summary:"
        Write-Verbose " Templates enabled: $($successful.Count)"
        Write-Verbose " Templates already enabled: $($alreadyEnabled.Count)"
        Write-Verbose " Failed operations: $($failed.Count)"
    }
}