Had an idea to write some (PowerShell) script which will check and maybe notify me of certificates that are nearing expiration for a bunch of (public) sites that… somewhat matter to me. 😊
As it turns out, someone already had this idea and wrote very nice PowerShell script that does just that, available here – thank you!
While testing it, there were sites on which the script worked just fine, and there were sites on which I got errors like this one (Error: “String was not recognized as a valid DateTime.”):
1 2 3 4 5 6 7 |
# Exception while checking URL https://blog.kaniski.eu: Exception calling "GetResponse" with "0" argument(s): "The operation has timed out" # Cannot convert value "26.4.2022. 1:59:59" to type "System.DateTime". Error: "String was not recognized as a valid DateTime." # At line:17 char:5 # + [datetime]$expiration = $req.ServicePoint.Certificate.GetExpirati ... # + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # + CategoryInfo : MetadataError: (:) [], ArgumentTransformationMetadataException # + FullyQualifiedErrorId : RuntimeException |
Seems to be connected to my regional settings (I know… who would ever use hr-HR instead of en-US, but… 😊) and date/time formatting:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
PS C:\> (Get-Culture).DateTimeFormat # AMDesignator : # Calendar : System.Globalization.GregorianCalendar # DateSeparator : . # FirstDayOfWeek : Monday # CalendarWeekRule : FirstDay # FullDateTimePattern : d. MMMM yyyy. H:mm:ss # LongDatePattern : d. MMMM yyyy. # LongTimePattern : H:mm:ss # MonthDayPattern : d. MMMM # PMDesignator : # RFC1123Pattern : ddd, dd MMM yyyy HH':'mm':'ss 'GMT' # ShortDatePattern : d.M.yyyy. # ShortTimePattern : H:mm # SortableDateTimePattern : yyyy'-'MM'-'dd'T'HH':'mm':'ss # TimeSeparator : : # UniversalSortableDateTimePattern : yyyy'-'MM'-'dd HH':'mm':'ss'Z' # YearMonthPattern : MMMM, yyyy # AbbreviatedDayNames : {ned, pon, uto, sri...} # ShortestDayNames : {ne, po, ut, sr...} # DayNames : {nedjelja, ponedjeljak, utorak, srijeda...} # AbbreviatedMonthNames : {sij, vlj, ožu, tra...} # MonthNames : {siječanj, veljača, ožujak, travanj...} # IsReadOnly : False # NativeCalendarName : gregorijanski kalendar # AbbreviatedMonthGenitiveNames : {sij, vlj, ožu, tra...} # MonthGenitiveNames : {siječnja, veljače, ožujka, travnja...} |
I’ve tried to fix it in a couple of ways, but the one that finally did it (for me) was explained on Dan Sheehan’s blog (thanks!), implemented on lines 25-26 below.
So, my adapted script looks like this (and works with my hr-HR culture):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
$minimumCertAgeDays = 30 $timeoutMilliseconds = 6000 $urls = @( "https://blog.kaniski.eu/", "https://www.google.com/", "https://www.microsoft.com/" ) # disabling the cert validation check. This is what makes this whole thing work with invalid certs... [Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } foreach ($url in $urls) { Write-Host "Getting certificate information for $url ..." -ForegroundColor "Yellow" $req = [System.Net.WebRequest]::Create($url) $req.Timeout = $timeoutMilliseconds try { $req.GetResponse() | Out-Null } catch { Write-Host "Exception occurred while checking URL $url`: $_ ." -ForegroundColor "Red" } $expirationString = $req.ServicePoint.Certificate.GetExpirationDateString() $dateTimeFormat = "$((Get-Culture).DateTimeFormat.ShortDatePattern) $((Get-Culture).DateTimeFormat.LongTimePattern)" $expiration = [DateTime]::ParseExact($expirationString, $dateTimeFormat, [System.Globalization.DateTimeFormatInfo]::InvariantInfo, [System.Globalization.DateTimeStyles]::None) [int]$certExpiresIn = ($expiration - $(Get-Date)).Days if ($certExpiresIn -gt $minimumCertAgeDays){ Write-Host "Certificate for site $url expires in $certExpiresIn days (on $('{0:dd.MM.yyyy.}' -f $expiration))." -ForegroundColor "Green" } else { Write-Host "ERROR: Certificate for site $url expires in $certExpiresIn days (on $('{0:dd.MM.yyyy.}' -f $expiration)) and query threshold is set to $minimumCertAgeDays days!" -ForegroundColor "Red" } } |
It provides the following output (which can be further customized per your needs, of course… and I know – need to insert some line breaks, convert output to HTML, send it via e-mail, … it’s a start! 😊):
1 2 3 4 5 6 7 8 |
# ... # Getting certificate information for https://blog.kaniski.eu/ ... # Certificate for site https://blog.kaniski.eu/ expires in 110 days (on 26.04.2022.). # Getting certificate information for https://www.google.com/ ... # Certificate for site https://www.google.com/ expires in 46 days (on 21.02.2022.). # Getting certificate information for https://www.microsoft.com/ ... # Certificate for site https://www.microsoft.com/ expires in 204 days (on 28.07.2022.). # ... |
Note that I’m returning expiration date “the Croatian way”, by using the following formatting:
1 |
$('{0:dd.MM.yyyy.}' -f $expiration) |
Hope it helps someone (and #kudos to original authors)!
Cheers!
Is it possible to write this script that outputs to a cvs or text file?
Good question, Jacke! It is – you can see the example of it at https://github.com/TomicaKaniski/toms-notes-code/blob/main/20220105_Checking%20certificate%20expiration%20with%20PowerShell/5_check-certificate-expiration-with-csv.ps1. Hope it helps!
Is it possible get an email if the certificate will expire in x days only?
Of course! You would then adjust the script to check and list only certificates that will expire in x days only, schedule it daily, and add a portion to send you an email with the list (or do nothing, if no certificates are expiring in x days).
Tom