Private/Get-ADCSEnterpriseCA.ps1

function Get-ADCSEnterpriseCA {

    ################################################################################
    ##### #####
    ##### Get all Enterprise CAs from AD #####
    ##### #####
    ################################################################################

    Param(
        [Parameter(ValueFromPipeline, Mandatory)]
        [string]$Server
    )

    $CurrentFunction = Get-FunctionName
    Write-Log -Message "### Start Function $CurrentFunction ###"
    $StartRunTime = (Get-Date).ToString($Script:DateFormatLog)
    #################### main code | out- host #####################

    # Get all Enterprise CAs from AD

    $ConfigNC = (Get-ADRootDSE).ConfigurationNamingContext

    $cas = Get-ADObject -LDAPFilter "(objectClass=pKIEnrollmentService)" `
        -SearchBase "CN=Public Key Services,CN=Services,$ConfigNC"  `
        -Properties cn, dNSHostName, cACertificate, certificateTemplates, flags   `
        -Server $Server

    # Helper: build X509 objects from cACertificate (handles collections)
    function Get-CaCertificates {
        param([Microsoft.ActiveDirectory.Management.ADObject]$CaObject)

        $certs = @()

        foreach ($entry in $CaObject.cACertificate) {
            if ($entry -is [byte[]]) {
                try {
                    $certs += [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($entry)
                }
                catch {}
            }
        }

        $certs
    }

    # Build CA inventory with cert info
    $inventory = foreach ($ca in $cas) {
        $certs = Get-CaCertificates -CaObject $ca

        foreach ($cert in $certs) {
            [PSCustomObject]@{
                CAName       = $ca.cn
                Server       = $ca.dNSHostName
                Subject      = $cert.Subject
                Issuer       = $cert.Issuer
                Thumbprint   = $cert.Thumbprint
                NotBefore    = $cert.NotBefore
                NotAfter     = $cert.NotAfter
                IsSelfSigned = ($cert.Subject -eq $cert.Issuer)
                HasTemplates = ($ca.certificateTemplates -ne $null -and $ca.certificateTemplates.Count -gt 0)
                AD_Flags     = $ca.flags
            }
        }
    }

    # Build hierarchy: link issuing CAs to their parent by Subject/Issuer
    $roots = $inventory | Where-Object { $_.IsSelfSigned }
    $issuing = $inventory | Where-Object { -not $_.IsSelfSigned }

    $hierarchy = foreach ($ca in $inventory) {
        $parent = $null
        if (-not $ca.IsSelfSigned) {
            $parent = $roots + $issuing | Where-Object { $_.Subject -eq $ca.Issuer } | Select-Object -First 1
        }

        [PSCustomObject]@{
            CAName       = $ca.CAName
            Server       = $ca.Server
            Subject      = $ca.Subject
            Issuer       = $ca.Issuer
            IsRootCA     = $ca.IsSelfSigned
            HasTemplates = $ca.HasTemplates
            ParentCA     = if ($parent) { $parent.CAName } else { $null }
        }
    }

    # Check online/offline (simple ping)
    $servers = $hierarchy.Server | Sort-Object -Unique
    $status = foreach ($s in $servers) {
        $online = Test-Connection -ComputerName $s -Count 1 -Quiet -ErrorAction SilentlyContinue
        [PSCustomObject]@{
            Server = $s
            Online = $online
        }
    }

    # Join status back into hierarchy
    $final = $hierarchy | ForEach-Object {
        $st = $status | Where-Object { $_.Server -eq $_.Server } | Select-Object -First 1
        [PSCustomObject]@{
            CAName       = $_.CAName
            Server       = $_.Server
            Online       = $st.Online
            IsRootCA     = $_.IsRootCA
            HasTemplates = $_.HasTemplates
            ParentCA     = $_.ParentCA
            Subject      = $_.Subject
            Issuer       = $_.Issuer
        }
    }

    # Nice overview

    Invoke-Output -Type H1 -Message "Identitfied Enterprise CAs [$($final.count)]"
    if ($null -ne $final) {
        $final | Sort-Object IsRootCA, CAName | Format-Table CAName, Server, Online, HasTemplates, IsRootCA, ParentCA
        $Script:ADCSEnterpriseCAMissing = $false
    }
    else {
        Invoke-Output -Type Info -Message "No Enterprise CA found."
    }


    $4logfile = $final | Select-Object CAName, Server, Online, HasTemplates, IsRootCA, ParentCA | Sort-Object IsRootCA, CAName | Format-Table | Out-String
    Write-Log -Message " >> Identified the following Get Enterprise CAs under Forest '$ConfigNC': $4logfile"


    ######################## main code ############################
    $runtime = Get-RunTime -StartRunTime $StartRunTime
    Write-Log -Message " Run Time: $runtime [h] ###"
    Write-Log -Message "### End Function $CurrentFunction ###"
}