Powershell script to get certificate expiry for a website remotely for multiple servers

bhanu prakash picture bhanu prakash · Aug 31, 2016 · Viewed 26.2k times · Source

I am trying to create an script to get the certificate expiry date for an websites remotely for multiple servers. I have an script which is working for single server (Need to login into server and doing execution), I need to run this remotely for multiple servers. How can i modify this script to execute for multiple servers remotely. Please advice.

 $servers = get-content D:\Certificate.txt
$DaysToExpiration = 60 #change this once it's working 
$expirationDate = (Get-Date).AddDays($DaysToExpiration)

foreach ($server in $servers)
{

$sites = Get-Website | ? { $_.State -eq "Started" } | % { $_.Name }
$certs = Get-ChildItem IIS:SSLBindings | ? {
           $sites -contains $_.Sites.Value
         } | % { $_.Thumbprint }


Get-ChildItem CERT:LocalMachine/My | ? {
  $certs -contains $_.Thumbprint -and $_.NotAfter -lt $expirationDate
}
}

Answer

Jochen picture Jochen · Aug 22, 2017

Inspired by https://iamoffthebus.wordpress.com/2014/02/04/powershell-to-get-remote-websites-ssl-certificate-expiration/ I use following script:

$minimumCertAgeDays = 60
$timeoutMilliseconds = 10000
$urls = get-content .\check-urls.txt

#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 Checking $url -f Green
    $req = [Net.HttpWebRequest]::Create($url)
    $req.Timeout = $timeoutMilliseconds
    $req.AllowAutoRedirect = $false
    try {$req.GetResponse() |Out-Null} catch {Write-Host Exception while checking URL $url`: $_ -f Red}
    $certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
    #Write-Host "Certificate expires on (string): $certExpiresOnString"
    [datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
    #Write-Host "Certificate expires on (datetime): $expiration"
    [int]$certExpiresIn = ($expiration - $(get-date)).Days
    $certName = $req.ServicePoint.Certificate.GetName()
    $certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
    $certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
    $certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
    $certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
    $certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
    if ($certExpiresIn -gt $minimumCertAgeDays)
    {
        Write-Host Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green
    }
    else
    {
        Write-Host WARNING: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Red
        Write-Host Threshold is $minimumCertAgeDays days. Check details:`nCert name: $certName -f Red
        Write-Host Cert public key: $certPublicKeyString -f Red
        Write-Host Cert serial number: $certSerialNumber`nCert thumbprint: $certThumbprint`nCert effective date: $certEffectiveDate`nCert issuer: $certIssuer -f Red
    }
    Write-Host
    rv req
    rv expiration
    rv certExpiresIn
}

Alternatively, you might find this advanced script useful:

  • you can switch between report output as Text, Html or PSObject
  • use the script with urls (parameter array) or with input file for urls or with pipeline input
  • improved stability: correctly handle missing certificates on HTTP connections
  • just put the code into a file like Check-ExpiringSslCerts.ps1

Here the advanced script code:

[CmdletBinding(DefaultParametersetname="URLs in text file")]
Param(
  [ValidateSet('Text','Html','PSObject')]
  [string]$ReportType = 'Text',
  [int]$MinimumCertAgeDays = 60,
  [int]$TimeoutMilliseconds = 10000,
  [parameter(Mandatory=$false,ParameterSetName = "URLs in text file")]
  [string]$UrlsFile = '.\check-urls.txt',
  [parameter(Mandatory=$false,ParameterSetName = "List of URLs", 
        ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
  [string[]]$Urls
)
Begin 
{
    [string[]]$allUrls = @()
    $returnData = @()
    [bool]$ProcessedInputPipeLineByArrayItem = $false

    function CheckUrl ([string]$url, [array]$returnData)
    {
        [string]$details = $null
        if ($ReportType -eq "Html") 
        { 
            $stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($url)
            Write-Host "<tr><td>$stringHtmlEncoded</td>" 
        }
        if ($ReportType -eq "Text") { Write-Host Checking $url }
        $req = [Net.HttpWebRequest]::Create($url)
        $req.Timeout = $timeoutMilliseconds
        $req.AllowAutoRedirect = $false
        try 
        {
            $req.GetResponse() |Out-Null
            if ($req.ServicePoint.Certificate -eq $null) {$details = "No certificate in use for connection"}
        } 
        catch 
        {
            $details = "Exception while checking URL $url`: $_ "
        }
        if ($details -eq $null -or $details -eq "")
        {
            $certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
            #Write-Host "Certificate expires on (string): $certExpiresOnString"
            [datetime]$expiration = [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
            #Write-Host "Certificate expires on (datetime): $expiration"
            [int]$certExpiresIn = ($expiration - $(get-date)).Days
            $certName = $req.ServicePoint.Certificate.GetName()
            $certPublicKeyString = $req.ServicePoint.Certificate.GetPublicKeyString()
            $certSerialNumber = $req.ServicePoint.Certificate.GetSerialNumberString()
            $certThumbprint = $req.ServicePoint.Certificate.GetCertHashString()
            $certEffectiveDate = $req.ServicePoint.Certificate.GetEffectiveDateString()
            $certIssuer = $req.ServicePoint.Certificate.GetIssuerName()
            if ($certExpiresIn -gt $minimumCertAgeDays)
            {
                if ($ReportType -eq "Html") 
                { 
                    Write-Host "<td>OKAY</td><td>$certExpiresIn</td><td>$expiration</td><td>&nbsp;</td></tr>"
                }
                if ($ReportType -eq "Text") 
                { 
                    Write-Host OKAY: Cert for site $url expires in $certExpiresIn days [on $expiration] -f Green 
                }
                if ($ReportType -eq "PSObject") 
                { 
                    $returnData += new-object psobject -property  @{Url = $url; CheckResult = "OKAY"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = [string]$null}
                }
            }
            else
            {
                $details = ""
                $details += "Cert for site $url expires in $certExpiresIn days [on $expiration]`n"
                $details += "Threshold is $minimumCertAgeDays days. Check details:`n"
                $details += "Cert name: $certName`n"
                $details += "Cert public key: $certPublicKeyString`n"
                $details += "Cert serial number: $certSerialNumber`n"
                $details += "Cert thumbprint: $certThumbprint`n"
                $details += "Cert effective date: $certEffectiveDate`n"
                $details += "Cert issuer: $certIssuer"
                if ($ReportType -eq "Html") 
                { 
                    Write-Host "<td>WARNING</td><td>$certExpiresIn</td><td>$expiration</td>"
                    $stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
                    Write-Host "<tr><td>$stringHtmlEncoded</td></tr>" 
                }
                if ($ReportType -eq "Text") 
                { 
                    Write-Host WARNING: $details -f Red
                }
                if ($ReportType -eq "PSObject") 
                { 
                    $returnData += new-object psobject -property  @{Url = $url; CheckResult = "WARNING"; CertExpiresInDays = [int]$certExpiresIn; ExpirationOn = [datetime]$expiration; Details = $details}
                }
            rv expiration
            rv certExpiresIn
            }
        }
        else
        {
            if ($ReportType -eq "Html") 
            { 
                Write-Host "<td>ERROR</td><td>N/A</td><td>N/A</td>"
                $stringHtmlEncoded = [System.Web.HttpUtility]::HtmlEncode($details) -replace "`n", "<br />"
                Write-Host "<tr><td>$stringHtmlEncoded</td></tr>" 
            }
            if ($ReportType -eq "Text") 
            { 
                Write-Host ERROR: $details -f Red
            }
            if ($ReportType -eq "PSObject") 
            { 
                $returnData += new-object psobject -property  @{Url = $url; CheckResult = "ERROR"; CertExpiresInDays = $null; ExpirationOn = $null; Details = $details}
            }
        }
        if ($ReportType -eq "Text") { Write-Host }
        rv req
        return $returnData
    }

    #disabling the cert validation check. This is what makes this whole thing work with invalid certs...
    [Net.ServicePointManager]::ServerCertificateValidationCallback = {$true}

    if ($ReportType -eq "Html") 
    { 
        Write-Host "<table><tr><th>URL</th><th>Check result</th><th>Expires in days</th><th>Expires on</th><th>Details</th></tr>" 
        Add-Type -AssemblyName System.Web 
    }
}
Process
{
    if ($_ -ne $null)
    {
        CheckUrl $_ $returnData
        $ProcessedInputPipeLineByArrayItem = $true
    }
}
End
{
    if ($ProcessedInputPipeLineByArrayItem -eq $false)
    {
        if ($Urls -eq $null)
        {
            $allUrls = get-content $UrlsFile
        }
        else
        {
            $allUrls = $Urls
        }
        foreach ($url in $allUrls)
        {
            $returnData = CheckUrl $url $returnData
        }
    }
    if ($ReportType -eq "Html") { Write-Host "</table>" }
    if ($ReportType -eq "PSObject") { return $returnData }
}

Output might look like e.g.:

"http://www.doma.com", "https://www.domb.com" | .\Check-ExpiringSslCerts.ps1 -ReportType PSObject | ft

Url                  ExpirationOn        CertExpiresInDays CheckResult Details                             
---                  ------------        ----------------- ----------- -------                              
http://www.doma.com                                        ERROR       No certificate in use for connection 
https://www.domb.com 18.11.2017 09:33:00 87                OKAY