Functions/CertificateManagement/Get-FpsCertificate.ps1

<#
.SYNOPSIS
    Returns all installed certificates that matches with the given ThumbPrint or SubjectFilter.
.DESCRIPTION
    Default this cmdlet searches for certificates that have a private key. Enable IncludePublicCertificates to search for certificates with only a public key too.
.EXAMPLE
    Get-FpsCertificate -ReturnCertObject
.EXAMPLE
    Get-FpsCertificate -ThumbPrint '008CEE1FEA5RANDOM2AF4F603EBPRINTBB0341D1'
.EXAMPLE
    Get-FpsCertificate -CertificatePath 'cert:\LocalMachine\WebHosting' -SubjectFilter 'MySubject*' -IncludePublicCertificates
#>

function Get-FpsCertificate {
    [CmdletBinding(DefaultParameterSetName='SearchWithSubjectFilter')]  
    param(
        # The certificate unique thumbprint
        [Parameter(Mandatory=$true, ParameterSetName='SearchWithThumbPrint', ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true)]
        [ValidatePattern('^[a-zA-Z\d]+$')]
        [string] $ThumbPrint,
        
        # A subject filter to find certificates. E.g. 'MySubject*' returns all certificates where the subject starts with 'MySubject'.
        # Default value is: '*4ps*'
        [Parameter(ParameterSetName='SearchWithSubjectFilter', ValueFromPipelineByPropertyName=$true)]
        [string] $SubjectFilter = '*4ps*',    
        
        # The certificate provider path where to scan for certificates. Path should start with 'Cert:'. E.g. 'cert:\LocalMachine\WebHosting'.
        # Default value is: 'cert:\LocalMachine\My'
        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [string] $CertStorePath = 'cert:\LocalMachine\My',
        
        # When enabled this cmdlet returns the X509Certificate2 certificate object instead of a new powershell object.
        # Note: When enabled additional properties like UsedOnIISWebSites and UsedOnBcServerInstances are not added on the returned object anymore.
        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [switch] $ReturnCertObject,

        # Enable IncludePublicCertificates to search for Public and Private installed certificates.
        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [switch] $IncludePublicCertificates,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [switch] $SkipScanWebsiteBindings, 

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [switch] $SkipScanBcConfig
    )

    # Test if the get-website cmdlet is available on the host.
    $SkipScanWebsiteBindings =  if((Get-Command get-website -ErrorAction SilentlyContinue) -and 
                                   $SkipScanWebsiteBindings -ne $true){
                                        $false
                                } else {$true}
    
    # Get the certificate based on either the thumprint or subjectfilter.
    if(-not [string]::IsNullOrEmpty($ThumbPrint)){
        $certificates = Get-ChildItem -path $CertStorePath | Where-Object -Property Thumbprint -eq $ThumbPrint
    } else {
        $certificates = Get-ChildItem -path $CertStorePath | Where-Object subject -like $SubjectFilter
    }
    
    if(-not $IncludePublicCertificates){
        $certificates = $certificates | Where-Object -Property HasPrivateKey -eq $true
    }

    # $certificates = $certificates | Where-Object -Property FriendlyName -ne ''

    if($ReturnCertObject){
        return $certificates
    }

    if(!$SkipScanBcConfig){
        $bcServerInstances = Get-BCServerInstance
        $bcServerInstances | ForEach-Object {$_.AppSettings = New-Object psobject -Property $_.AppSettings} # work-around: Convert hashtable to psobject
    }
    if(!$SkipScanWebsiteBindings){
        $WebSites = Get-Website
    }
    
    foreach ($certificate in $certificates){
        
        # Add IIS WebSite names to certificate object where the certificate is used in the https bindings
        if(!$SkipScanWebsiteBindings){
            
            $WebSitesWithCertBinding = $WebSites | ForEach-Object {
                foreach ($binding in $_.bindings.Collection){
                    if($binding.protocol -eq 'https' -and $binding.certificateHash -eq $certificate.Thumbprint){
                        $_
                    }
                }
            }
            $certificate | Add-Member -NotePropertyName 'UsedOnIISWebSites' -NotePropertyValue $WebSitesWithCertBinding.Name
        } else {
            Write-Verbose 'Scan IIS website bindings is disabled.'
        }

        # Add Business Central ServerInstances names to certificate object where the certificate is used
        if(!$SkipScanBcConfig){
            $certificate | Add-Member -NotePropertyName 'UsedOnBcServerInstances' -NotePropertyValue `
                ($bcServerInstances | Where-Object {$_.AppSettings.ServicesCertificateThumbprint -eq $certificate.Thumbprint}).ServerInstance
        } else {
            Write-Verbose 'Scan Business Central ServerInstances is disabled.'
        }

        # Add users to certificate object with Read access on the certificate
        Write-Verbose 'Searching for users with read access on the certificate...'

        $certObj = Get-FpsCertificate -ThumbPrint $certificate.Thumbprint -CertStorePath $CertStorePath -ReturnCertObject
        if($certObj){
            $certPath = '{0}\Microsoft\Crypto\RSA\MachineKeys\{1}' -f 
                $env:ALLUSERSPROFILE, 
                [System.Security.Cryptography.X509Certificates.RSACertificateExtensions]::GetRSAPrivateKey($certObj).key.UniqueName
        } else {
            $certPath = ''
        }
        
        Write-Verbose ('Certificate path for certificate ''{0}'': {1}' -f $certificate.Thumbprint, $certPath)

        if($certPath){
            $certificate | Add-Member -NotePropertyName 'UsersWithReadAccess' -NotePropertyValue `
                ((Get-Acl -Path $certPath).Access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.FileSystemRights -match 'FullControl|Read' -and $_.IdentityReference -notlike 'S-*'}).IdentityReference.Value
        }
        # Add expiration information to certificate object
        $certificate | Add-Member -NotePropertyName 'IsExpired' -NotePropertyValue ($certificate.NotAfter -lt (Get-Date))
        
        $DaysToExpiration = ($certificate.NotAfter - (Get-Date)).Days
        if([double]$DaysToExpiration -lt 0){$DaysToExpiration = 0}
        $certificate | Add-Member -NotePropertyName 'DaysToExpiration' -NotePropertyValue $DaysToExpiration
    }

    # Converts X509Certificate2 to PSCustom objects and select most usefull properties
    return $certificates | Select-Object FriendlyName, Subject, Thumbprint, Issuer, HasPrivateKey, NotBefore, NotAfter, DaysToExpiration, IsExpired, UsedOnIISWebSites, UsedOnBcServerInstances, UsersWithReadAccess
}

Export-ModuleMember -Function Get-FpsCertificate