#PSTip Deleting expired certificates from the personal certificate store

We use a lot of certificates for website authentication, and they expire each year.  Some people end up with a collection of expired certificates. That makes it hard to find the right one when presented with the list of certificates in Internet Explorer.

To work with the certificates we use the X.509 Certificate Provider (Microsoft.PowerShell.Security\Certificate).  This provider in PowerShell 2.0 requires jumping through a few manual hoops to clean up the environment.  We need to work with the System.Security.Cryptography.X509Certificates.X509Store object associated with the store and grab that with Get-Item:

$myCerts = Get-Item Cert:\CurrentUser\My

To delete the certificates we have to open the $myCerts X.509 Store object passing an OpenFlags enumeration.  The Open() method can create a new store or set what access is given to the given store based on the OpenFlags.  By default the store is read-only and you cannot remove the certificate without opening it.

Member nameDescription
IncludeArchivedOpen the X.509 certificate store and include archived certificates.
MaxAllowedOpen the X.509 certificate store for the highest access allowed.
OpenExistingOnlyOpens only existing stores; if no store exists, the Open method will not create a new store.
ReadOnlyOpen the X.509 certificate store for reading only.
ReadWriteOpen the X.509 certificate store for both reading and writing.
$myCerts.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite)

To get the list of expired certificates we need to filter the child items that are not valid after yesterday.  $myCerts is already pointing to the path we need, so we can use it as a reference rather than repeating a hard coded string.

$today = Get-Date
$ExpiredList = Get-ChildItem $myCerts.PSPath | Where-Object { $_.NotAfter -lt $today }

There is a reason we set _$toda_y before the pipeline,  Get-Date does work, we don’t want to call if for every iteration of the Where-Object cmdlet as we don’t need the refined difference between each call.

Remove each certificate in the X.509 certificate store that was returned from our query:

ForEach ($Cert in $ExpiredList) {
	$myCerts.Remove($Cert)
}

$myCerts.Close() # We opened it, so we need to close it.

In PowerShell 3.0 the entire thing can be done:

$today = Get-Date
Get-ChildItem Cert:\CurrentUser\My |
Where-Object NotAfter -lt $today |
Remove-Item

#or

Get-ChildItem Cert:\CurrentUser\My |
ForEach-Object -begin { $now = get-date } -process { if ($PSItem.NotAfter -lt $now ) { $PSItem } } |
Remove-Item
Share on: