Solving the June 2025 Scripting Challenge
At the end of last month, I left you with a new scripting challenge. These challenges are intended to give you a reason to use PowerShell to solve a practical problem. Now, you may not need to solve this particular problem, but the process you experience while working through the challenge will help you learn how to use PowerShell to solve other problems in the future.
The challenge I left for you focused on the Certificate provider in PowerShell. I gave you a few tasks to complete using PowerShell. Here's how I tackled the challenge. As always, my code is not the only way or even the best way to solve the problem. Regardless, I hope you find it informative.
Finding Expired Certificates
The first task was to find expired certificates on the local computer. The Certificate provider exposes the certificate store through a PSDrive called Cert
. We add the colon when we reference the drive.
PS C:\> dir cert:\
Location : CurrentUser
StoreNames : {[SmartCardRoot, True], [AuthRoot, True], [UserDS, True],
[Disallowed, True]...}
Location : LocalMachine
StoreNames : {[My, True], [WindowsServerUpdateServices, True],
[TrustedPublisher, True], [eSIM Certification Authorities, True]...}
Since this might be a new area, you might want to explore and discover. I always get a sample object to see what properties are available.
PS C:\> dir Cert:\CurrentUser\my | Select-Object -First 1 -Property *
PSPath : Microsoft.PowerShell.Security\Certificate::CurrentUse
r\my\BFCDBFE0CF7331D41BDA7D58377128A4E833BF13
PSParentPath : Microsoft.PowerShell.Security\Certificate::CurrentUse
r\my
PSChildName : BFCDBFE0CF7331D41BDA7D58377128A4E833BF13
PSDrive : Cert
PSProvider : Microsoft.PowerShell.Security\Certificate
PSIsContainer : False
EnhancedKeyUsageList : {}
DnsNameList : {9972287b-4dca-4c85-ba67-1531014e7e8d}
SendAsTrustedIssuer : False
EnrollmentPolicyEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndP
ointProperty
EnrollmentServerEndPoint : Microsoft.CertificateServices.Commands.EnrollmentEndP
ointProperty
PolicyId :
Archived : False
Extensions : {}
FriendlyName : Microsoft Your Phone
HasPrivateKey : True
PrivateKey :
IssuerName : System.Security.Cryptography.X509Certificates.X500Dis
tinguishedName
NotAfter : 3/21/2026 8:38:03 AM
NotBefore : 3/20/2025 8:38:03 PM
PublicKey : System.Security.Cryptography.X509Certificates.PublicK
ey
RawData : {48, 130, 1, 145...}
RawDataMemory : System.ReadOnlyMemory<byte>[405]
SerialNumber : 00E84342D84715ED6C
SignatureAlgorithm : System.Security.Cryptography.Oid
SubjectName : System.Security.Cryptography.X509Certificates.X500Dis
tinguishedName
Thumbprint : BFCDBFE0CF7331D41BDA7D58377128A4E833BF13
Version : 3
Handle : 1491086180048
Issuer : CN=9972287b-4dca-4c85-ba67-1531014e7e8d
Subject : CN=9972287b-4dca-4c85-ba67-1531014e7e8d
SerialNumberBytes : System.ReadOnlyMemory<byte>[9]
I could use Get-Member
but Select-Object
shows my the property name and value. Based on this output, I can see the NotAfter
and NotBefore
properties. These look appropriate for my task.
dir cert: -Recurse |
where { $_.NotAfter -and ($_.NotAfter -lt (Get-Date)) } |
Sort-Object -Property NotAfter |
Select-Object -Property NotAfter, Subject,
@{Name="Path";Expression={Join-Path -Path "Cert:" -childPath (Convert-Path $_.PSPath)}},
@{Name="Computername";Expression = {$Env:COMPUTERNAME}} |
Format-List
The certificate object doesn't have an explicit path property. I'm constructing one using Join-Path
and Convert-Path
. The latter cmdlet converts the PSPath
property into something more "filesystem-like."
NotAfter : 5/8/2021 4:40:55 PM
Subject : CN=Token Signing Public Key
Path : Cert:\LocalMachine\Windows Live ID Token Issuer\2C85006A1A028BCC349DF23C474724C055FDE8B6
Computername : CADENZA
NotAfter : 5/9/2021 7:28:13 PM
Subject : CN=Microsoft Root Certificate Authority, DC=microsoft, DC=com
Path : Cert:\LocalMachine\Root\CDD4EEAE6000AC7F40C3802C171E30148030C072
Computername : CADENZA
I am not limiting my search to user or machine certificates. The code is searching the entire certificate store.
Finding Expiring Certificates
The next task was to find certificates that will expire in the next 90 or 180 days. On my laptop, I had to increase to 360 days to get results.
PS C:\> dir Cert: -Recurse -ExpiringInDays 360 | sort NotAfter |
Select-Object NotAfter,
@{Name="Path";Expression={Join-Path -Path "Cert:" -childPath (Convert-Path $_.PSPath)}},
@{Name="Computername";Expression = {$Env:COMPUTERNAME}} |
Format-List
NotAfter : 12/30/1999 6:59:59 PM
Path : Cert:\LocalMachine\Root\245C97DF7514E7CF2DF8BE72AE957B9E04741E85
Computername : CADENZA
NotAfter : 12/30/1999 6:59:59 PM
Path : Cert:\CurrentUser\Root\245C97DF7514E7CF2DF8BE72AE957B9E04741E85
Computername : CADENZA
...
You can see that I have been using Get-Childitem
(aliased as dir
) to explore the certificate store. The -Recurse
parameter allows me to search through all the sub-stores just as I with files and folders. The -ExpiringInDays
parameter is a dynamic parameter that the Certificate provider adds to the Get-ChildItem
cmdlet. You may not see it in the help unless you are in the a Cert: location. Fortunately, the help for Get-ChildItem
explicitly documents this parameter.
PS C:\> help Get-ChildItem -Parameter ExpiringInDays
-ExpiringInDays <system.int32>
> [!NOTE] > This parameter is only available in the > Certificate
(../Microsoft.PowerShell.Security/About/about_Certificate_Provider.md)provider.
Specifies that the cmdlet should only return certificates that are expiring in or before the specified number of days. A value of zero (`0`)
gets certificates that have expired.
This parameter was reintroduced in PowerShell 7.1
Required? false
Position? named
Default value None
Accept pipeline input? False
Aliases none
Accept wildcard characters? false
This isn't always the case, especially in Windows PowerShell. You should change location to the PSDrive you are working in and look at help for the commands you want to use. There may be a dynamic parameter that you can use to simplify your code.