Private/Set/Set-CAComputerPrincipal.ps1
|
function Set-CAComputerPrincipal { <# .SYNOPSIS Adds CA identification and computer principal properties to AD CS Certification Authority objects. .DESCRIPTION For each pKIEnrollmentService (CA) object, looks up the corresponding computer principal in Active Directory using the dNSHostName property. This allows correlation between CA objects and their host computer accounts for security analysis. This function adds two synthetic properties to each CA object: - CAFullName: Combined display name in format "CAName (dNSHostName)" - ComputerPrincipal: The SID of the computer account hosting the CA, or $null if not found The function uses New-GCSearcher for Global Catalog lookups and Resolve-Principal for consistent principal resolution and caching in the PrincipalStore. .PARAMETER AdcsObject One or more DirectoryEntry objects representing AD CS Certification Authorities. .INPUTS DirectoryEntry objects representing pKIEnrollmentService objects. .OUTPUTS DirectoryEntry objects with added ComputerPrincipal property. .EXAMPLE $cas | Set-CAComputerPrincipal .NOTES This function must be called after Get-AdcsObject has populated the AdcsObjectStore with CA objects. It requires the dNSHostName property on CA objects to locate the corresponding computer account. The computer principal is looked up using a Global Catalog search for efficiency in multi-domain forests. #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [System.DirectoryServices.DirectoryEntry[]]$AdcsObject ) begin { Write-Verbose "Identifying computer principals for Certification Authorities..." } process { $AdcsObject | Where-Object SchemaClassName -eq pKIEnrollmentService | ForEach-Object { try { # Extract CA name - check both DirectoryEntry Properties and direct property $caName = if ($_.Properties -and $_.Properties.Contains('cn')) { $_.Properties['cn'][0] } elseif ($_.cn) { $_.cn } else { $null } # Extract dNSHostName - check both DirectoryEntry Properties and direct property $dnsHostName = if ($_.Properties -and $_.Properties.Contains('dNSHostName')) { $_.Properties['dNSHostName'][0] } elseif ($_.dNSHostName) { $_.dNSHostName } else { $null } Write-Verbose "Processing CA: $caName" if (-not $dnsHostName) { Write-Verbose " CA '$caName' has no dNSHostName property - cannot locate computer principal" $computerSID = $null } else { Write-Verbose " CA '$caName' is hosted on: $dnsHostName" # Search for the computer object by dNSHostName using existing infrastructure try { Write-Verbose " Searching for computer object with dNSHostName = '$dnsHostName'" # Use New-GCSearcher for consistent Global Catalog lookups $gcSearcher = New-GCSearcher -Filter "(&(objectClass=computer)(dNSHostName=$dnsHostName))" -PropertiesToLoad @('objectSid', 'distinguishedName', 'sAMAccountName') if ($gcSearcher) { $result = $gcSearcher.FindOne() if ($result) { $computerDN = $result.Properties['distinguishedname'][0] Write-Verbose " Found computer object: $computerDN" # Get the SID and convert to SecurityIdentifier $sidBytes = $result.Properties['objectsid'][0] $sid = New-Object System.Security.Principal.SecurityIdentifier($sidBytes, 0) $computerSID = $sid.Value Write-Verbose " Computer SID: $computerSID" # Resolve and cache the computer principal for later security auditing # This populates PrincipalStore so subsequent nTSecurityDescriptor audits don't require LDAP queries $principal = Resolve-Principal -IdentityReference $sid Write-Verbose " Cached computer principal in PrincipalStore: $computerSID" } else { Write-Verbose " Computer object not found for dNSHostName '$dnsHostName'" $computerSID = $null } $gcSearcher.Dispose() } else { Write-Verbose " Failed to create GC searcher" $computerSID = $null } } catch { Write-Verbose " Failed to lookup computer principal: $_" $computerSID = $null } } # Update the AD CS Object Store with ComputerPrincipal property # Note: CAFullName is now a ScriptProperty on LS2AdcsObject and calculates automatically $dn = $_.Properties.distinguishedName[0] if ($script:AdcsObjectStore.ContainsKey($dn)) { $script:AdcsObjectStore[$dn].ComputerPrincipal = $computerSID Write-Verbose "Updated AD CS Object Store for $dn with ComputerPrincipal = $computerSID" } # Also add to the pipeline object for backward compatibility $_ | Add-Member -NotePropertyName ComputerPrincipal -NotePropertyValue $computerSID -Force # Return the modified object $_ } catch { $errorRecord = [System.Management.Automation.ErrorRecord]::new( $_.Exception, 'CAComputerPrincipalProcessingFailed', [System.Management.Automation.ErrorCategory]::InvalidOperation, $ca ) $PSCmdlet.WriteError($errorRecord) # Still return the object even if processing failed $_ } } } end { Write-Verbose "Done identifying computer principals for Certification Authorities." } } |