Skip to content

Feature: test ONLINE for https certificates that are about to expire #137

Description

@ndemou

Here's how to use the function below

C:\> Get-SslCertificateDaysRemaining -Url https://wiki.enlogic.gr
197 # days remaining until cert expires

I can add this function to helpers for custom health tests.

I can write a custom health test on DC01 & DC02 until I have the mechanism
to only run a test once per domain at which point I can add it to the
domain and the first domain server that runs will execute it and
add a flag so that the others don't run it too.

function Get-SslCertificateDaysRemaining {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$Url,

        [ValidateRange(1, 300)]
        [int]$TimeoutSeconds = 10
    )

    if ($Url -notmatch '^[a-z][a-z0-9+.-]*://') {
        $Url = "https://$Url"
    }

    try {
        $uri = [Uri]$Url
    }
    catch {
        throw "Invalid URL: $Url"
    }

    if ($uri.Scheme -ne 'https') {
        throw "The URL must use HTTPS: $Url"
    }

    $serverName = $uri.DnsSafeHost
    $port = if ($uri.IsDefaultPort) { 443 } else { $uri.Port }

    $tcpClient = $null
    $sslStream = $null
    $asyncWaitHandle = $null

    try {
        $tcpClient = New-Object System.Net.Sockets.TcpClient
        $asyncResult = $tcpClient.BeginConnect(
            $serverName,
            $port,
            $null,
            $null
        )

        $asyncWaitHandle = $asyncResult.AsyncWaitHandle

        if (-not $asyncWaitHandle.WaitOne($TimeoutSeconds * 1000)) {
            throw "Connection to $($serverName):$port timed out."
        }

        $tcpClient.EndConnect($asyncResult)

        $validationCallback =
            [System.Net.Security.RemoteCertificateValidationCallback] {
                param(
                    $sender,
                    $certificate,
                    $chain,
                    $sslPolicyErrors
                )

                return $true
            }

        $sslStream = New-Object System.Net.Security.SslStream(
            $tcpClient.GetStream(),
            $false,
            $validationCallback
        )

        $sslStream.AuthenticateAsClient($serverName)

        if ($null -eq $sslStream.RemoteCertificate) {
            throw "The server did not provide an SSL certificate."
        }

        $certificate =
            New-Object System.Security.Cryptography.X509Certificates.X509Certificate2(
                $sslStream.RemoteCertificate
            )

        [math]::Floor(
            (
                $certificate.NotAfter.ToUniversalTime() -
                [DateTime]::UtcNow
            ).TotalDays
        )
    }
    finally {
        if ($null -ne $asyncWaitHandle) {
            $asyncWaitHandle.Dispose()
        }

        if ($null -ne $sslStream) {
            $sslStream.Dispose()
        }

        if ($null -ne $tcpClient) {
            $tcpClient.Close()
        }
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions