PowerShell Certificates: Manage SSL Certificates on Windows

PowerShell Certificates: Manage SSL Certificates on Windows

PowerShell Tips Editor 1 min read
PowerShell Certificates: Manage SSL Certificates on Windows

An expired SSL certificate brings down HTTPS-dependent services with no warning — one day it works, the next your monitoring tools start screaming. A scheduled PowerShell script that checks certificate expiry across all your servers and sends alerts 30 days before the deadline costs an hour to build and saves you from emergency midnight scrambles. Knowing how to PowerShell manage SSL certificates also means you can export, import, and audit your entire PKI estate programmatically. This post covers the Cert: drive, expiry detection, PFX export and import, and the alert script pattern.

Navigate the Cert: Drive

PowerShell exposes the Windows certificate store as a PSDrive named Cert:, which you can navigate exactly like a file system. The top-level folders are CurrentUser and LocalMachine:

# List the top-level stores
Get-ChildItem Cert:

# Navigate into machine store locations
Get-ChildItem Cert:\LocalMachine

# List all stores under LocalMachine
Get-ChildItem Cert:\LocalMachine | Select-Object Location, Name
Location     Name
--------     ----
LocalMachine AddressBook
LocalMachine AuthRoot
LocalMachine My
LocalMachine Root
LocalMachine TrustedPublisher
LocalMachine WebHosting

List Certificates in a Store

The My store (also called “Personal”) contains certificates with private keys. The WebHosting store is where IIS binds certificates by default:

Get-ChildItem Cert:\LocalMachine\My |
    Select-Object Subject, Thumbprint, NotAfter, HasPrivateKey |
    Format-Table -AutoSize

# Search by subject
Get-ChildItem Cert:\LocalMachine\My |
    Where-Object Subject -like "*corp.com*" |
    Select-Object Subject, Thumbprint, NotAfter
Subject                        Thumbprint                               NotAfter
-------                        ----------                               --------
CN=web01.corp.com, O=Corp Inc  A1B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8  6/30/2026 12:00:00 AM
CN=api.corp.com, O=Corp Inc    B2C3D4E5F6A7B8C9D0E1F2A3B4C5D6E7F8A1  3/15/2026 12:00:00 AM

Find Expiring Certificates

Retrieve all certificates expiring within a date threshold and return them as objects for further processing or alerting:

$daysWarning = 60
$threshold   = (Get-Date).AddDays($daysWarning)

$expiring = Get-ChildItem -Path Cert:\LocalMachine\My -Recurse |
    Where-Object { $_.NotAfter -le $threshold -and $_.NotAfter -gt (Get-Date) } |
    Select-Object Subject, Thumbprint, NotAfter,
        @{N='DaysRemaining'; E={ ($_.NotAfter - (Get-Date)).Days }}

if ($expiring) {
    Write-Warning "Found $($expiring.Count) certificate(s) expiring within $daysWarning days:"
    $expiring | Format-Table -AutoSize
} else {
    Write-Host "No certificates expiring within $daysWarning days"
}

Export a Certificate to PFX

Export a certificate with its private key to a PFX file for backup or migration. Exporting the private key requires the current user to have access to it:

$cert     = Get-ChildItem Cert:\LocalMachine\My |
    Where-Object Subject -like "*web01.corp.com*" | Select-Object -First 1

$pfxPath  = "C:\Certs\web01-backup.pfx"
$password = Read-Host "Enter PFX password" -AsSecureString

$cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, $password) |
    Set-Content -Path $pfxPath -Encoding Byte

Write-Host "Exported to $pfxPath"

Import a Certificate

Import a PFX file into a certificate store using the Import-PfxCertificate cmdlet (Windows 8/Server 2012 and later):

$pfxPath  = "C:\Certs\web01-new.pfx"
$password = Read-Host "PFX password" -AsSecureString

$imported = Import-PfxCertificate -FilePath $pfxPath `
    -CertStoreLocation Cert:\LocalMachine\My `
    -Password $password `
    -Exportable

Write-Host "Imported: $($imported.Subject)"
Write-Host "Thumbprint: $($imported.Thumbprint)"
Write-Host "Valid until: $($imported.NotAfter)"

Alert on Certificates Expiring in 30 Days

Combine the expiry check with an email alert and schedule it as a daily task:

$threshold = (Get-Date).AddDays(30)
$expiring  = Get-ChildItem Cert:\LocalMachine\My |
    Where-Object { $_.NotAfter -le $threshold -and $_.NotAfter -gt (Get-Date) }

if ($expiring) {
    $body = "Certificates expiring within 30 days on $env:COMPUTERNAME:`n`n"
    $body += ($expiring | ForEach-Object {
        "$($_.Subject) — expires $($_.NotAfter.ToString('yyyy-MM-dd')) ($( ($_.NotAfter - (Get-Date)).Days ) days)"
    }) -join "`n"

    Send-MailMessage -From "[email protected]" -To "[email protected]" `
        -Subject "CERT EXPIRY WARNING: $env:COMPUTERNAME" `
        -Body $body -SmtpServer "smtp.corp.com"
}

Common Errors and Fixes

  • Cert: drive shows user and machine stores — specify path carefully. Cert:\LocalMachine\My and Cert:\CurrentUser\My are different stores. IIS uses LocalMachine. Running a script as a regular user may find no certificates if you check CurrentUser when the certificate is in LocalMachine.
  • Exporting the private key requires the right permissions. Certificates imported without -Exportable cannot have their private key exported. If you get an error exporting, check that the certificate was imported with the exportable flag and that your account has access to the private key via the certificate’s key storage provider.

Related Cmdlets / See Also

Wrapping Up

The Cert: drive makes certificate management as intuitive as file system operations in PowerShell. Build a daily expiry alert script, schedule it on each server or run it centrally across your estate, and you will never be surprised by an expired certificate again. Export certificates before migrations and import them with -Exportable when you may need to move them again.

Send-Item -To