Home > Blog > PowerShell: Create a Certificate Expiration Warning

The unexpected expiration of critical server certificates has plagued IT organizations ever since the dawn of their existence. An expired certificate has the possibility of causing the failure of SSL-encrypted communications, triggering those annoying browser warnings about untrusted certificates. In a worst-case scenario, like when the expired certificate is SharePoint’s Security Token Service Signing Certificate, any ability for users to authenticate into the SharePoint environment comes to a screeching halt, leaving every site in the farm a veritable ghost-town, complete with tumbleweeds and ominously circling vultures.

Being able to stay aware of upcoming expiration dates is not always an easy task. While many of the most commonly used certificate issuers provide email notifications of such potential occurrences, oftentimes those messages go ignored because they might be sent to the single person that originally started the relationship with the certificate issuer, someone who might not even be with the organization any longer, or might not have any knowledge of the impact of expired certificates and how to go about renewing them before they expire.

Using PowerShell, we can create an automated system that can gather the information about every certificate in a farm and export that information into a centralized report as well as send email notifications when certificates are about to expire. This ensures that the IT professionals responsible for ensuring the validity of those certificates don’t have to manually click through possibly dozens to hundreds of nodes in each server’s certificate manager interface just to find those upcoming expiration dates.

How It’s Done

The crux of the PowerShell script that provides this warning system is the following cmdlet:

Get-ChildItem Cert:\LocalMachine -Recurse

This cmdlet is what iterates though all the local server’s certificates in every certificate directory. This cmdlet can be stored to a variable which can further be iterated through to parse out the individual properties of each certificate. The expiration date of a certificate is represented by the NotAfter property. Therefore, calculating the number of days until a certificate expires can be performed by storing the current date as a variable and then using the New-TimeSpan cmdlet to provide the difference between the current date and the NotAfter property, like this:

$now = Get-Date

$daysUntilExpiration = (New-TimeSpan -Start $now -End $_.NotAfter).Days

After that, it’s just a matter of extracting other useful pieces of information about a certificate from properties such as PSPath, Subject, Issuer, Thumbprint, FriendlyName, and NotBefore . PowerShell’s Out-File cmdlet can easily send that information to a CSV file for simple reporting. A more complete version of such a script might look something like this:

Providing Added Functionality

This script provides a starting point for monitoring the validity of a server’s certificates. A few extra lines of code can extend the functionality even further to create a farm-wide automated warning system. Here are some examples of ways to expand on this:

·Wrap this code within a script block using the Invoke-Command cmdlet so this script can run on remote servers and return certificate information for the entire farm.

·Report if a certificate is used for an IIS site binding by importing the WebAdministration module, retrieve the server’s IIS bindings, and linking the bindings and certificates with the Thumbprint property of each as a primary key.

·Schedule the script to run daily to refresh the report automatically using Task Scheduler .

·Send certificate expiration warning messages by email using PowerShell’s Send-MailMessage cmdlet when the daysUntilExpiration variable reaches a certain number.

Worrying about server certificates expiring should no longer keep IT professionals awake at night. They have harder challenges to contend with already in this ever-changing landscape. With some simple PowerShell, certificate information is easily accessible and can provide the kind of actionable data that allows you to prevent unwanted downtime rather than be forced to respond to disasters after they happen.


[wpforms id="1422"]
<div class="wpforms-container wpforms-container-full" id="wpforms-1422"><form id="wpforms-form-1422" class="wpforms-validate wpforms-form" data-formid="1422" method="post" enctype="multipart/form-data" action="/blog/powershell-create-a-certificate-expiration-warning-system" data-token="02a3e52e1f88e4640a52ae3eaf408d12"><noscript class="wpforms-error-noscript">Please enable JavaScript in your browser to complete this form.</noscript><div class="wpforms-field-container"><div id="wpforms-1422-field_0-container" class="wpforms-field wpforms-field-name" data-field-id="0"><label class="wpforms-field-label" for="wpforms-1422-field_0">Full Name <span class="wpforms-required-label">*</span></label><input type="text" id="wpforms-1422-field_0" class="wpforms-field-large wpforms-field-required" name="wpforms[fields][0]" required></div><div id="wpforms-1422-field_1-container" class="wpforms-field wpforms-field-email" data-field-id="1"><label class="wpforms-field-label" for="wpforms-1422-field_1">Email <span class="wpforms-required-label">*</span></label><input type="email" id="wpforms-1422-field_1" class="wpforms-field-large wpforms-field-required" name="wpforms[fields][1]" required></div><div id="wpforms-1422-field_3-container" class="wpforms-field wpforms-field-phone" data-field-id="3"><label class="wpforms-field-label" for="wpforms-1422-field_3">Phone <span class="wpforms-required-label">*</span></label><input type="tel" id="wpforms-1422-field_3" class="wpforms-field-large wpforms-field-required wpforms-masked-input" data-inputmask="&#039;mask&#039;: &#039;(999) 999-9999&#039;" data-rule-us-phone-field="true" data-inputmask-inputmode="tel" name="wpforms[fields][3]" required></div><div id="wpforms-1422-field_2-container" class="wpforms-field wpforms-field-textarea" data-field-id="2"><label class="wpforms-field-label" for="wpforms-1422-field_2">How can we help you? <span class="wpforms-required-label">*</span></label><textarea id="wpforms-1422-field_2" class="wpforms-field-small wpforms-field-required" name="wpforms[fields][2]" required></textarea></div><div id="wpforms-1422-field_15-container" class="wpforms-field wpforms-field-html" data-field-id="15"><div id="wpforms-1422-field_15"><p style="padding-top: 10px; padding-bottom: 10px; font-family: 'Open Sans', sans-serif; font-size: 10px; color: #333333;">By clicking the button below, you are agreeing to our <span style="text-decoration: underline;"><span style="color: #000000;"><a style="color: #000000; text-decoration: underline;" href="https://www.incworx.com/privacy-policy">Privacy Policy</a></span></span>.</p></div></div><div id="wpforms-1422-field_7-container" class="wpforms-field wpforms-field-hidden" data-field-id="7"><input type="hidden" id="wpforms-1422-field_7" name="wpforms[fields][7]" value="PowerShell: Create a Certificate Expiration Warning"></div><div id="wpforms-1422-field_9-container" class="wpforms-field wpforms-field-hidden" data-field-id="9"><input type="hidden" id="wpforms-1422-field_9" name="wpforms[fields][9]" value=""></div><div id="wpforms-1422-field_10-container" class="wpforms-field wpforms-field-hidden" data-field-id="10"><input type="hidden" id="wpforms-1422-field_10" name="wpforms[fields][10]"></div></div><div class="wpforms-field wpforms-field-hp"><label for="wpforms-1422-field-hp" class="wpforms-field-label">Website</label><input type="text" name="wpforms[hp]" id="wpforms-1422-field-hp" class="wpforms-field-medium"></div><div class="wpforms-submit-container" ><input type="hidden" name="wpforms[id]" value="1422"><input type="hidden" name="wpforms[author]" value="2"><input type="hidden" name="wpforms[post_id]" value="641"><button type="submit" name="wpforms[submit]" class="wpforms-submit " id="wpforms-submit-1422" value="wpforms-submit" aria-live="assertive" data-alt-text="Sending..." data-submit-text="Submit">Submit</button></div></form></div> <!-- .wpforms-container -->