Private/Set/Set-CACertificateManager.ps1

function Set-CACertificateManager {
    <#
        .SYNOPSIS
        Adds Certificate Manager role configuration to AD CS Certification Authority objects.
 
        .DESCRIPTION
        For each pKIEnrollmentService (CA) object, queries the CA's Certificate Manager
        role assignments using PSCertutil's Get-PCCertificateManager cmdlet and stores the
        results in the AdcsObjectStore.
         
        Certificate Managers can approve or deny certificate requests, revoke certificates,
        and perform other certificate lifecycle management tasks. Excessive or inappropriate
        Certificate Manager assignments can lead to ESC7 vulnerabilities.
         
        The function adds these properties to each CA object:
        - CertificateManagers: Array of all Certificate Manager objects returned by Get-PCCertificateManager
 
        .PARAMETER AdcsObject
        One or more DirectoryEntry objects representing AD CS Certification Authorities.
        Must have the CAFullName property set (typically by Set-CAComputerPrincipal).
 
        .INPUTS
        System.DirectoryServices.DirectoryEntry[]
        You can pipe CA DirectoryEntry objects to this function.
 
        .OUTPUTS
        System.DirectoryServices.DirectoryEntry[]
        Returns the input objects with added CertificateManagers property.
 
        .EXAMPLE
        $cas | Set-CACertificateManager
        Queries Certificate Managers for all CAs and stores the results.
 
        .EXAMPLE
        $cas | Set-CAComputerPrincipal | Set-CACertificateManager
        Sets up CA identification and then queries Certificate Managers.
 
        .NOTES
        Requires the PSCertutil module to be installed and loaded.
        This function must be called after Set-CAComputerPrincipal has set the CAFullName property.
         
        The function silently skips CAs that:
        - Don't have a CAFullName property
        - Are unreachable or don't respond to certutil queries
        - Return errors from Get-PCCertificateManager
 
        .LINK
        https://posts.specterops.io/certified-pre-owned-d95910965cd2
    #>

    [CmdletBinding()]
    [OutputType([System.DirectoryServices.DirectoryEntry[]])]
    param (
        [Parameter(Mandatory, ValueFromPipeline)]
        [System.DirectoryServices.DirectoryEntry[]]$AdcsObject
    )

    #requires -Version 5.1

    begin {
        Write-Verbose "Querying Certificate Managers for Certification Authorities..."
    }

    process {
        $AdcsObject | Where-Object SchemaClassName -eq pKIEnrollmentService | ForEach-Object {
            try {
                # Extract CA name for logging
                $caName = if ($_.Properties -and $_.Properties.Contains('cn')) {
                    $_.Properties['cn'][0]
                } elseif ($_.cn) {
                    $_.cn
                } else {
                    'Unknown CA'
                }
                
                Write-Verbose "Processing CA: $caName"
                
                # Get CAFullName from the AdcsObjectStore (where LS2AdcsObject has CAFullName ScriptProperty)
                $dn = $_.Properties.distinguishedName[0]
                $caFullName = if ($script:AdcsObjectStore.ContainsKey($dn)) {
                    $script:AdcsObjectStore[$dn].CAFullName
                } else {
                    $null
                }
                
                if (-not $caFullName) {
                    Write-Verbose " CA '$caName' has no CAFullName property - skipping Certificate Managers query"
                    $_
                    return
                }
                
                Write-Verbose " Querying Certificate Managers for: $caFullName"
                
                try {
                    # Query Certificate Managers using PSCertutil
                    $certificateManagers = Get-PCCertificateManager -CAFullName $caFullName -ErrorAction Stop
                    
                    if ($certificateManagers) {
                        $managerCount = @($certificateManagers).Count
                        Write-Verbose " Retrieved $managerCount Certificate Manager(s)"
                        
                        # Resolve and cache each manager in PrincipalStore
                        foreach ($manager in $certificateManagers) {
                            Write-Verbose " Certificate Manager: $($manager.CertificateManager)"
                            
                            try {
                                # Convert the string to an NTAccount and resolve to cache in PrincipalStore
                                $ntAccount = New-Object System.Security.Principal.NTAccount($manager.CertificateManager)
                                $null = Resolve-Principal -IdentityReference $ntAccount
                                Write-Verbose " Cached principal in PrincipalStore"
                            } catch {
                                Write-Verbose " Failed to resolve principal: $($_.Exception.Message)"
                            }
                        }
                        
                        # Update the AD CS Object Store
                        $dn = $_.Properties.distinguishedName[0]
                        if ($script:AdcsObjectStore.ContainsKey($dn)) {
                            $script:AdcsObjectStore[$dn].CertificateManagers = $certificateManagers
                            Write-Verbose " Updated AD CS Object Store for $dn with Certificate Manager data"
                        }
                        
                        # Also add to the pipeline object for backward compatibility
                        $_ | Add-Member -NotePropertyName CertificateManagers -NotePropertyValue $certificateManagers -Force
                        
                    } else {
                        Write-Verbose " No Certificate Managers returned from Get-PCCertificateManager"
                    }
                    
                } catch {
                    Write-Verbose " Failed to query Certificate Managers for '$caFullName': $($_.Exception.Message)"
                    # Continue processing other CAs
                }
                
                # Return the modified object
                $_
                
            } catch {
                Write-Warning "Error processing CA: $($_.Exception.Message)"
                $_
            }
        }
    }

    end {
        Write-Verbose "Certificate Manager query complete"
    }
}