functions/CA/Get-PkiCaCertificate.ps1
function Get-PkiCaCertificate { <# .SYNOPSIS Lists certificates from a CA. .DESCRIPTION Lists certificates from a CA. .PARAMETER Type What kind of certificate to return. Defaults to "Issued" .PARAMETER Disposition The "Type" of a certificate is not _really_ a thing. In truth, the CA has two tables - Queue & Log - and different codes for the different states that a certificate in either may be in. The views in the MMC console are just an abstraction layer on top of that. The Disposition "code" describes what state the certificate is currently in. Numbers and their meaning: 8 request is being processed 9 request is taken under submission 12 certificate is an archived foreign certificate 15 certificate is a CA certificate 16 parent CA certificates of the CA certificate 17 certificate is a key recovery agent certificate 20 certificate was issued 21 certificate is revoked 30 certificate request failed 31 certificate request is denied .PARAMETER ComputerName The computername of the CA (automatically detects the CA name) Specifying this will cause the command to use PowerShell remoting. .PARAMETER Credential The credentials to use when connecting to the server. Only used in combination with -ComputerName. .PARAMETER FQCAName The fully qualified name of the CA. Specifying this allows remote access to the target CA. '<Computername>\<CA Name>' .PARAMETER CommonName Filter by common name. .PARAMETER RequestID Search for a certificate by its specific request ID. .PARAMETER Requester Search for certificates by who requested them. .PARAMETER TemplateName Search for certificates by the template they were made from. .PARAMETER Properties The properties to retrieve. These are the headers as shown in the CA mmc console on an English languaged device. The result objects will have the same properties, but without the whitespace. .PARAMETER Server The active directory server to contact using LDAP. Used to resolve the templates used. .EXAMPLE PS C:\> Get-PkiCaCertificate Returns all issued certificates from the current computer (assumes localhost is a CA) .EXAMPLE PS C:\> Get-PkiCaCertificate -FQCAName "ca.contoso.com\MS-CA-01" Returns all issued certificates from the CA "ca.contoso.com\MS-CA-01" Requires the local computer to have the CA management tools installed .EXAMPLE PS C:\> Get-PkiCaCertificate -Computername ca.contoso.com Returns all issued certificate from ca.contoso.com. Requires PS remoting access to the target computerh osting the CA service. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingEmptyCatchBlock", "")] [CmdletBinding()] param ( [ValidateSet('Issued', 'Request', 'Revoked', 'Failed', 'Denied')] [string] $Type = 'Issued', [PsfArgumentCompleter({ 'Request', 'Issued', 'Revoked', 'Failed', 'Denied' })] [PSFramework.Utility.ScriptTransformation('PkiExtension.Disposition', [int])] [int] $Disposition, [PSFComputer[]] $ComputerName, [PSCredential] $Credential, [string] $FQCAName, [string] $CommonName, [int] $RequestID, [string] $Requester, [PsfArgumentCompleter('PkiExtension.TemplateName')] [string] $TemplateName, [PsfArgumentCompleter('PkiExtension.CertificateProperties')] [string[]] $Properties, [string] $Server ) begin { $tmplParam = $PSBoundParameters | ConvertTo-PSFHashtable -Include Server, Credential $templates = Get-PkiTemplate @tmplParam $dispositions = @{ Denied = 31 Failed = 30 Revoked = 21 Issued = 20 Request = 9 } #region Properties $propertyHash = @{ Default = @( 'Issued Common Name' 'Certificate Expiration Date' 'Certificate Effective Date' 'Certificate Template' 'Issued Request ID' 'Certificate Hash' 'Request Disposition Message' 'Requester Name' 'Binary Certificate' ) Issued = @( 'Issued Request ID' 'Issued Common Name' 'Certificate Expiration Date' 'Certificate Effective Date' 'Certificate Template' 'Certificate Hash' 'Request Disposition Message' 'Requester Name' 'Binary Certificate' ) Revoked = @( 'Issued Request ID' 'Revocation Date' 'Effective Revocation Date' 'Revocation Reason' 'Request Disposition Message' 'Requester Name' 'Binary Certificate' 'Certificate Expiration Date' 'Certificate Effective Date' 'Certificate Template' 'Certificate Hash' ) Request = @( 'Request ID' 'Binary Request' 'Request Status Code' 'Request Disposition Message' 'Request Submission Date' 'Requester Name' 'Request Common Name' 'Certificate Template' ) Denied = @( 'Request ID' 'Binary Request' 'Request Status Code' 'Request Disposition Message' 'Request Submission Date' 'Requester Name' 'Request Common Name' 'Certificate Template' ) Failed = @( 'Request ID' 'Binary Request' 'Request Status Code' 'Request Disposition Message' 'Request Submission Date' 'Requester Name' 'Request Common Name' 'Certificate Template' ) } #endregion Properties $data = @{ FQCAName = $FQCAName Properties = $propertyHash[$Type] Templates = $templates CommonName = $CommonName RequestID = $RequestID Requester = $Requester TemplateName = $TemplateName Disposition = $dispositions[$Type] } if ($Properties) { $data.Properties = $Properties } if ($Disposition) { $data.Disposition = $Disposition if (-not $Properties) { switch ($Disposition) { 9 { $data.Properties = $propertyHash['Request'] } 20 { $data.Properties = $propertyHash['Issued'] } 21 { $data.Properties = $propertyHash['Revoked'] } 30 { $data.Properties = $propertyHash['Failed'] } 31 { $data.Properties = $propertyHash['Denied'] } default { $data.Properties = $propertyHash['Default'] } } } } $parameters = @{ ArgumentList = $data } if ($ComputerName) { $parameters["HideComputerName"] = $true $parameters["ComputerName"] = $ComputerName if ($Credential) { $parameters['Credential'] = $Credential } } } process { Invoke-PSFCommand @parameters -ScriptBlock { param ( $Data ) # Copy variables over foreach ($pair in $Data.GetEnumerator()) { Set-Variable -Name $pair.Key -Value $pair.Value } $ErrorActionPreference = 'Stop' trap { Write-Warning "Error retrieving Certificate information: $_" throw $_ } #region Preparation CA Connect try { $caView = New-Object -ComObject CertificateAuthority.View } catch { throw "Unable to create Certificate Authority View. $env:COMPUTERNAME does not have ADSC Installed" } if ($FQCAName) { $null = $CaView.OpenConnection($FQCAName) } else { $caName = (Get-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Services\CertSvc\Configuration' -Name Active).Active $null = $caView.OpenConnection("$($env:COMPUTERNAME)\$($caName)") } $CaView.SetResultColumnCount($Properties.Count) foreach ($item in $Properties) { $index = $caView.GetColumnIndex($false, $item) $caView.SetResultColumn($index) } # https://learn.microsoft.com/en-us/windows/win32/api/certview/nf-certview-icertview-setrestriction $CVR_SEEK_EQ = 1 # $CVR_SEEL_LE = 2 # $CVR_SEEK_LT = 4 # $CVR_SEEK_GE = 8 # $CVR_SEEK_GT = 16 $CVR_SORT_NONE = 0 # 20 - issued certificates $caView.SetRestriction($caView.GetColumnIndex($false, 'Request Disposition'), $CVR_SEEK_EQ, $CVR_SORT_NONE, $Data.Disposition) if ($CommonName) { $caView.SetRestriction($caView.GetColumnIndex($false, 'Issued Common Name'), $CVR_SEEK_EQ, $CVR_SORT_NONE, $CommonName) } if ($RequestID) { $caView.SetRestriction($caView.GetColumnIndex($false, 'Issued Request ID'), $CVR_SEEK_EQ, $CVR_SORT_NONE, $RequestID) } if ($Requester) { $caView.SetRestriction($caView.GetColumnIndex($false, 'Requester Name'), $CVR_SEEK_EQ, $CVR_SORT_NONE, $Requester) } if ($TemplateName) { $templateID = ($Templates | Where-Object DisplayName -EQ $TemplateName).'msPKI-Cert-Template-OID' if (-not $templateID) { $templateID = $TemplateName } $caView.SetRestriction($caView.GetColumnIndex($false, 'Certificate Template'), $CVR_SEEK_EQ, $CVR_SORT_NONE, $templateID) } $CV_OUT_BASE64HEADER = 0 $CV_OUT_BASE64 = 1 $RowObj = $caView.OpenView() #endregion Preparation CA Connect #region Process Certificates while ($RowObj.Next() -ne -1) { #region Process Properties $Cert = @{ PSTypeName = "PkiExtension.IssuedCertificate" } $ColObj = $RowObj.EnumCertViewColumn() $null = $ColObj.Next() do { $displayName = $ColObj.GetDisplayName() # format Binary Certificate in a savable format. if ($displayName -eq 'Binary Certificate') { $Cert[$displayName.Replace(" ", "")] = $ColObj.GetValue($CV_OUT_BASE64HEADER) $Cert['Certificate'] = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new(([System.Text.Encoding]::UTF8.GetBytes($Cert[$displayName.Replace(" ", "")]))) } else { $Cert[$displayName.Replace(" ", "")] = $ColObj.GetValue($CV_OUT_BASE64) } } until ($ColObj.Next() -eq -1) Clear-Variable -Name ColObj #endregion Process Properties #region Process Template Name $Cert['TemplateDisplayName'] = $null if ($Cert.CertificateTemplate) { try { $Cert['TemplateDisplayName'] = ($Templates | Where-Object msPKI-Cert-Template-OID -EQ $Cert.CertificateTemplate).DisplayName if (-not $Cert['TemplateDisplayName']) { $Cert['TemplateDisplayName'] = ($Templates | Where-Object Name -EQ $Cert.CertificateTemplate).DisplayName } if (-not $Cert['TemplateDisplayName']) { $Cert['TemplateDisplayName'] = $Cert.CertificateTemplate } if ($Cert['Certificate']) { Add-Member -InputObject $Cert['Certificate'] -MemberType NoteProperty -Name TemplateDisplayName -Value $Cert['TemplateDisplayName'] } } catch { } } #endregion Process Template Name [pscustomobject]$Cert | Add-Member -MemberType ScriptMethod -Name ToString -Value { $this.IssuedCommonName } -Force -PassThru } #endregion Process Certificates } | Select-PSFObject -KeepInputObject -TypeName 'PkiExtension.IssuedCertificate' } } |