Collectors/Get-EscCaConfiguration.ps1

function Get-EscCaConfiguration {
    <#
    .SYNOPSIS
        Reads per-CA registry configuration via certutil -getreg (read-only).
    .DESCRIPTION
        For each supplied CA (Name + DnsHostName) runs `certutil -config
        "<host>\<CAName>" -getreg <value>` for the security-relevant values and
        maps the parsed output to the CaConfig schema:
            Name, DnsHostName, EditFlags, EditFlagsAttributeSanSet,
            InterfaceFlags, EnforceEncryptRequest, DisableExtensionList,
            SecurityAces(Ace[]), PolicyModule, Reachable

        certutil -getreg is a pure read. If certutil is unavailable or the host is
        unreachable, Reachable=$false and numeric/ACL fields are left null so the
        analyzers can mark ManualReview.

        Offline mode: pass -InputObject with records of the shape
            @{ Name=..; DnsHostName=..; Reachable=$true;
               RegText = @{ 'policy\EditFlags'='<text>'; 'CA\InterfaceFlags'='..';
                            'policy\DisableExtensionList'='..'; 'CA\Security'='..' } }
    .PARAMETER CA
        Array of CA descriptor objects (each with .Name and .DnsHostName), e.g.
        the output of Get-EscEnrollmentService.
    .PARAMETER InputObject
        Optional array of offline records (see description).
    .PARAMETER ExtraLowPrivSid
        Optional extra low-priv SIDs forwarded to the ACL parser.
    .OUTPUTS
        [pscustomobject] CaConfig[].
    .EXAMPLE
        Get-EscEnrollmentService | Get-EscCaConfiguration
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, ValueFromPipeline = $true)]
        [object[]] $CA,

        [Parameter(Mandatory = $false)]
        [object[]] $InputObject,

        [Parameter(Mandatory = $false)]
        [string[]] $ExtraLowPrivSid = @()
    )

    begin {
        $collected = New-Object System.Collections.ArrayList

        $valuePaths = @(
            'policy\EditFlags',
            'CA\InterfaceFlags',
            'policy\DisableExtensionList',
            'CA\Security',
            'CA\PolicyModules\Active'
        )

        if ($PSBoundParameters.ContainsKey('InputObject') -and $null -ne $InputObject) {
            Write-EscLog -Component 'CAConfig' -Message ("Offline mode: transforming {0} fixture record(s)." -f @($InputObject).Count)
            foreach ($rec in $InputObject) {
                $name = [string] $rec.Name
                $dns  = [string] $rec.DnsHostName
                $reachable = $true
                if ($null -ne $rec.Reachable) { $reachable = [bool] $rec.Reachable }
                $regText = @{}
                if ($null -ne $rec.RegText) {
                    if ($rec.RegText -is [hashtable]) { $regText = $rec.RegText }
                    else { foreach ($p in $rec.RegText.PSObject.Properties) { $regText[$p.Name] = $p.Value } }
                }
                [void]$collected.Add((ConvertTo-EscCaConfigObject -Name $name -DnsHostName $dns -RegText $regText -Reachable $reachable -ExtraLowPrivSid $ExtraLowPrivSid))
            }
        }
    }

    process {
        if ($PSBoundParameters.ContainsKey('InputObject')) { return }
        if ($null -eq $CA) { return }

        foreach ($caObj in $CA) {
            $name = [string] $caObj.Name
            $dns  = [string] $caObj.DnsHostName
            if ([string]::IsNullOrWhiteSpace($dns)) { $dns = $name }
            $config = '{0}\{1}' -f $dns, $name

            $secBytes = $null
            try {
                $localShort = [string] $env:COMPUTERNAME
                $localFqdn  = $localShort
                try { $localFqdn = [System.Net.Dns]::GetHostEntry($localShort).HostName } catch { }
                $dnsShort = ($dns -split '\.')[0]
                $isLocal = ($dns -ieq 'localhost' -or $dns -ieq $localShort -or $dns -ieq $localFqdn -or $dnsShort -ieq $localShort)
                if ($isLocal) {
                    $baseKey = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Default)
                    try {
                        $cfg = $baseKey.OpenSubKey('SYSTEM\CurrentControlSet\Services\CertSvc\Configuration')
                        if ($null -ne $cfg) {
                            $subs   = @($cfg.GetSubKeyNames())
                            $target = @($subs | Where-Object { $_ -ieq $name }) | Select-Object -First 1
                            if ([string]::IsNullOrWhiteSpace($target) -and $subs.Count -eq 1) { $target = $subs[0] }
                            if (-not [string]::IsNullOrWhiteSpace($target)) {
                                $caKey = $cfg.OpenSubKey($target)
                                if ($null -ne $caKey) {
                                    $sdVal = $caKey.GetValue('Security')
                                    if ($sdVal -is [byte[]] -and $sdVal.Length -gt 0) { $secBytes = $sdVal }
                                    $caKey.Close()
                                }
                            }
                            $cfg.Close()
                        }
                    }
                    finally { try { $baseKey.Close() } catch { } }
                }
            }
            catch {
                Write-EscLog -Component 'CAConfig' -Level Warning -Message ("Local registry read of CA security failed for '{0}': {1}" -f $name, $_.Exception.Message)
            }

            $regText = @{}
            $reachable = $true
            $anySuccess = $false

            foreach ($vp in $valuePaths) {
                $text = $null
                try {
                    $text = & certutil.exe -config $config -getreg $vp 2>&1 | Out-String
                    if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($text)) {
                        $regText[$vp] = $text
                        $anySuccess = $true
                    }
                }
                catch {
                    Write-EscLog -Component 'CAConfig' -Level Warning -Message ("certutil failed for {0} [{1}]: {2}" -f $config, $vp, $_.Exception.Message)
                }
            }

            if (-not $anySuccess) {
                $reachable = $false
                Write-EscLog -Component 'CAConfig' -Level Warning -Message ("CA '{0}' unreachable or certutil unavailable; marking Reachable=false." -f $config)
            }

            if ($null -ne $secBytes) { $reachable = $true }

            [void]$collected.Add((ConvertTo-EscCaConfigObject -Name $name -DnsHostName $dns -RegText $regText -Reachable $reachable -ExtraLowPrivSid $ExtraLowPrivSid -SecurityDescriptorBytes $secBytes))
        }
    }

    end {
        return @($collected.ToArray())
    }
}