When I run a compare operation on $certificateFromIssuer
within Get-IssuedCertificate
I get the proper output.
$certificateFromIssuer.NotAfter -lt $today
The above works as expected.
However, when $certificateFromIssuer
is returned to DeleteExpiredCertificates
I get the following error.
Cannot compare "@{NotAfter=8/28/2025 5:25:53 PM}" to "8/29/2024 2:56:25 PM" because the objects are not the same type or the object "@{NotAfter=8/28/2025 5:25:53 PM}" does not implement "IComparable".
Here's my full code:
function Get-IssuedCertificates {
param (
[Parameter(Mandatory = $true)]
$issuer
)
$certificates = Get-ChildItem -Path Cert:\CurrentUser\My
$certificatesFromIssuer = $certificates | Where-Object { $_.Issuer -like "*$issuer*" }
if ($certificatesFromIssuer) {
return ,$certificatesFromIssuer #| Format-Table -Property Subject, Issuer, Thumbprint, NotAfter
} else {
Write-Output "No certificates found from issuer: $issuer"
return @()
}
}
function DeleteExpiredCertificates {
param (
[Parameter(Mandatory = $false)]
$expiredCertificates
)
$today = Get-Date
if(($expiredCertificates | Select-Object -Property NotAfter) -lt $today) {
Write-Host "Certificate expired on: $($expiredCertificates | Select-Object -Property NotAfter)"
}
}
DeleteExpiredCertificates -expiredCertificates $(Get-IssuedCertificates -issuer Dexter)
Notice that, in the Delete function, I have to use $expiredCertificates | Select-Object -Property NotAfter
because $expiredCertificates.NotAfter
seems to return empty value which is also something I'd like to understand.
I'm expecting to understand why the datatype changes when a variable is returned from a function to another.
The main issue with your code is that you're trying to compare an array of objects with a property named NotAfter
(this is the output from Select-Object NotAfter
) with a DateTime
instance. You just need a loop and get rid of Select-Object
, you can use dot notation to reference a property value. Also note that [datetime]::Today
and Get-Date
are not the same, Get-Date
is [datetime]::Now
.
function DeleteExpiredCertificates {
param (
[Parameter(Mandatory)]
[X509Certificate[]] $ExpiredCertificates
)
$today = [datetime]::Today
foreach ($cert in $ExpiredCertificates) {
if ($cert.NotAfter -lt $today) {
Write-Host "Certificate expired on: $($cert.NotAfter)"
}
}
}
If you want your functions to be more PowerShell like, you can have your DeleteExpiredCertificates
take values from pipeline, so something like:
function Get-IssuedCertificates {
[OutputType([X509Certificate])]
param (
[Parameter(Mandatory)]
[string] $Issuer
)
$certificatesFromIssuer = Get-ChildItem -Path Cert:\CurrentUser\My |
Where-Object { $_.Issuer -like "*$Issuer*" }
if ($certificatesFromIssuer) {
return $certificatesFromIssuer
}
Write-Error "No certificates found from issuer: $Issuer"
}
function DeleteExpiredCertificates {
param (
[Parameter(Mandatory, ValueFromPipeline)]
[X509Certificate] $ExpiredCertificate
)
begin { $today = [datetime]::Today }
process {
if ($ExpiredCertificate.NotAfter -lt $today) {
Write-Host "Certificate expired on: $($ExpiredCertificate.NotAfter)"
}
}
}
And then you can pipe the output from one command to the other command:
Get-IssuedCertificates Dexter | DeleteExpiredCertificates