Skip Ribbon Commands
Skip to main content

Ondrej Sevecek's English Pages


Engineering and troubleshooting by Directory Master!
MCM: Directory

Quick Launch

Ondrej Sevecek's English Pages > Posts > How to remove expired user certificates from Active Directory user accounts using PowerShell
December 04
How to remove expired user certificates from Active Directory user accounts using PowerShell

The certification authority software of Active Directory Certificate Services (ADCS) running in the enterprise installation mode (AD integrated CA) can publish user certificates which it generates into the respective AD user account so that other users can find the certificates for their colleagues and use them for encryption. This function is usually completely unnecessary because only few environments use user certificates for any data encryption at all.

But yes, the default User certificate template has the setting called Publish certificate in Active Directory enabled by default which is then also the case for all duplicates created from this default User template.

The issued certificates get published in their DER binary form into the userCertificate multivalued attribute of their respective AD user object. Expired certificates are not removed automatically. If you want to find all user accounts in the local AD domain and remove any expired certificates from the accounts, you can use the following PowerShell script. The script not only deletes the expired certificate from the user account, it also saves the certificate into TEMP if that was for anything.

Note that the script handles only the published certificates stored in the userCertificate attribute (public certificates without private keys). It does not clean the certificates nor their private keys from the private credentials roaming msPKI attributes.

$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop

[object[]] $usersWithCerts = Get-ADUser -LDAPFilter '(userCertificate=*)' -Properties userCertificate

Write-Host ('Found user accounts with any certificate: #{0}' -f $usersWithCerts.Length)
foreach ($oneUserWithCert in $usersWithCerts) {

  Write-Host ('One user: {0} | certs = #{1} | {2}' -f $oneUserWithCert.sAMAccountName, $oneUserWithCert.userCertificate.Count, $oneUserWithCert.distinguishedName)

  foreach ($oneCertBin in $oneUserWithCert.userCertificate) {

    $oneCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2

    $isExpired = $oneCert.NotAfter -lt ([DateTime]::Now)
    Write-Host ('  certificate: {0} | expires = {1} (valid = {2}) | usage = {3}' -f $oneCert.Subject, $oneCert.NotAfter.ToString('yyyy-MM-dd HH:mm:ss'), (-not $isExpired), ($oneCert.EnhancedKeyUsageList -join ', '))

    if ($isExpired) {

      $saveExpired = Join-Path $env:TEMP ('expired-cert-{0}-exp{1}-{2}.cer' -f $oneUserWithCert.sAMAccountName, $oneCert.NotAfter.ToString('yyyy-MM-dd-HH-mm-ss'), $oneCert.Thumbprint)
      [void] ([System.IO.File]::WriteAllBytes($saveExpired, $oneCert.Export('Cert')))
      Write-Host ('  removing: saved = {0}' -f $saveExpired)

      Set-ADUser -Identity $oneUserWithCert -Certificates @{ Remove = $oneCert }



There are no comments for this post.

Add Comment

Sorry comments are disable due to the constant load of spam *

This simple antispam field seems to work well. Just put here the number.


You do not need to provide any value this column. It will automatically fill with the name of the article itself.

Author *

Body *