Certificates! The blog could probably end there but if there is something that always comes up when people are having issues, it's certificates. Mainly because people tend to forget them, don't monitor when they are expiring and then all hell breaks loose.

And when you actually add monitoring you would get a bunch of warnings about old certificates that are no longer valid. I got a case where this happened and in this case it was because they had a bunch of certificates laying around after changing their AD CS setup.

Let's go through the script section by section, then I'll summarize the entire script. I'll also show an example that will remove any certificates that is past the expiration date. In that version we will only be targeting the personal store as there are several root certificates that are expired that should be there.

The PowerShell, explained

First off, we set our variables. The two main components here are the list of servers and getting the credentials needed for this. We also have the issuer for the certificates that we want to remove.

$servers = (Get-AdComputer -Filter * -SearchBase "ou=servers,dc=domain,dc=local").DNSHostName
$issuer = "CN=CA, DC=domain, DC=local"
$cred = (Get-Credential)

You could provide a list, if there are only certain machines you want to run this script towards. I chose to get a list off all the computers in the server organizational unit so I could run this on all the servers.

Then, we run a foreach loop that connects to one server at the time and runs a script block.

foreach ($server in $servers){
    $session = New-PSSession -ComputerName $server -Credential $cred
    Invoke-Command -Session $session -ScriptBlock {
        Write-Output $env:COMPUTERNAME
    }
    Remove-PSSession $session
}

Here we are making a new PSSession based on the list of servers that we have and run Invoke-Command towards that machine. I added that it outputs the hostname of the server after connecting, just to have some visual clue to where it's at in the process.

We end by cleaning up, removing the PSSession. Next up, we actually do the work.

$certsMy = Get-ChildItem Cert:\LocalMachine\My | Where-Object -Property issuer -eq $issuer | Select-Object -ExpandProperty Thumbprint
foreach ($cert in $certsMy) {
	Remove-item cert:\LocalMachine\My\$cert
}
$certsRoot = Get-ChildItem Cert:\LocalMachine\Root | Where-Object -Property issuer -eq $issuer | Select-Object -ExpandProperty Thumbprint
foreach ($cert in $certsRoot) {
	Remove-item cert:\LocalMachine\Root\$cert
}

PowerShell has a built in drive for certificates called Cert so we can work with certificates as if they were any other files on the computer. We start first off with getting all the certificates that we want to remove by selecting the certificates based on the issuer. Then we use a foreach loop to remove the certificates.

In this case, we do it twice. Once for the certificates from the personal store (My when you browse with PSDrive) and once for the Trusted Root Certificate Authorities (Root).

Putting it all together

The entire script to get all the servers in an organizational unit and connect to each one of those to remove certificates from a certain CA.

$servers = (Get-AdComputer -Filter * -SearchBase "ou=servers,dc=domain,dc=local").DNSHostName
$issuer = "CN=CA, DC=domain, DC=local"
$cred = (Get-Credential)
foreach ($server in $servers){
    $session = New-PSSession -ComputerName $server -Credential $cred
    Invoke-Command -Session $session -ScriptBlock {
        Write-Output $env:COMPUTERNAME
        $certsMy = Get-ChildItem Cert:\LocalMachine\My | Where-Object -Property issuer -eq $issuer | Select-Object -ExpandProperty Thumbprint
        foreach ($cert in $certsMy) {
            Remove-item cert:\LocalMachine\My\$cert
        }
        $certsRoot = Get-ChildItem Cert:\LocalMachine\Root | Where-Object -Property issuer -eq $issuer | Select-Object -ExpandProperty Thumbprint
        foreach ($cert in $certsRoot) {
            Remove-item cert:\LocalMachine\Root\$cert
        }
    }
    Remove-PSSession $session
}

If we want to remove all certificates that is expired, we need to change the Where-Object to select the objects where the NotAfter property is less than the current date. Here's that example, but only towards the Personal store as I would be cautious removing some of the expired certificates in the Root store.

$servers = (Get-AdComputer -Filter * -SearchBase "ou=servers,dc=domain,dc=local").DNSHostName
$cred = (Get-Credential)
foreach ($server in $servers){
    $session = New-PSSession -ComputerName $server -Credential $cred
    Invoke-Command -Session $session -ScriptBlock {
        Write-Output $env:COMPUTERNAME
        $certsMy = Get-ChildItem Cert:\LocalMachine\My | Where-Object -Property notafter -lt (Get-Date) | Select-Object -ExpandProperty Thumbprint
        foreach ($cert in $certsMy) {
            Remove-item cert:\LocalMachine\My\$cert
        }
    }
    Remove-PSSession $session
}

Hope this was helpful for someone and if there are any questions, feel free to comment and I'll answer to the best of my abilities.