Powershell脚本,用于远程为多个服务器获取网站的证书到期日期

时间:2016-08-31 15:01:12

标签: powershell certificate iis-7.5 renewal

我正在尝试创建一个脚本,以便为多个服务器远程获取网站的证书到期日期。我有一个适用于单个服务器的脚本(需要登录到服务器并执行),我需要远程运行多个服务器。如何修改此脚本以远程执行多个服务器。请指教。

 $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
}
}

4 个答案:

答案 0 :(得分:1)

https://iamoffthebus.wordpress.com/2014/02/04/powershell-to-get-remote-websites-ssl-certificate-expiration/的启发我使用以下脚本:

$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
}

或者,您可能会发现此高级脚本很有用:

  • 您可以在报告输出之间切换为Text,Html或PSObject
  • 将脚本与urls(参数数组)或输入文件用于url或使用管道输入
  • 提高稳定性:正确处理HTTP连接上缺少的证书
  • 只需将代码放入Check-ExpiringSslCerts.ps1
  • 等文件中即可

这里是高级脚本代码:

[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 }
}

输出可能看起来像例如:

"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

答案 1 :(得分:0)

将你在脚本块中编写的整个代码放入,为了做到这一点,只需在开头添加此代码:

$sb = {

并在代码底部添加:

}

拥有此脚本块后,您可以使用以下命令在服务器上远程运行此脚本:

$cred = Get-Credential
$servers = get-content D:\Certificate.txt
Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock $SB

希望它有所帮助!

答案 2 :(得分:0)

您的代码段很有帮助,但是它们会在HTTP错误时引发错误-这是基础.NET HttpWebRequest对象的本质,它会惊慌于所有非代码200的内容。

因此,例如,如果您要测试仅接受POST HTTP动词的API端点,则脚本将失败,因为它将收到405错误消息。或者,如果您的URL返回HTTP 404,则脚本也会失败。幸运的是,无论如何,您都可以捕获第7层错误并捕获所需的信息,只需以这种方式修补代码即可:

try {$req.GetResponse() | Out-Null } catch {
    if ($_.Exception.InnerException.Status -eq 'ProtocolError') {

        # saving the info anyway since this is a L7 error, e.g.: 
        $certExpiresOnString = $req.ServicePoint.Certificate.GetExpirationDateString()
        [datetime]$expiration [System.DateTime]::Parse($req.ServicePoint.Certificate.GetExpirationDateString())
        $certIssuer = $req.ServicePoint.Certificate.GetIssuerName() # ...

    } else {

        # this is a real error - timeout, DNS failure etc
        Write-Host "$url, $_" -ForegroundColor Red
        Continue
    }       
}

答案 3 :(得分:0)

主要取自https://gist.github.com/jstangroome/5945820,尽管有所变化。以下将获得证书。然后需要检查$certificate.NotBefore$certificate.NotAfter

function GetCertificate([string]$domain, [Int16]$port) {
    $certificate = $null
    $TcpClient = New-Object -TypeName System.Net.Sockets.TcpClient
    $TcpClient.ReceiveTimeout = 1000
    $TcpClient.SendTimeout = 1000

    try {

        $TcpClient.Connect($domain, $port)
        $TcpStream = $TcpClient.GetStream()

        $Callback = { param($sender, $cert, $chain, $errors) return $true }

        $SslStream = New-Object -TypeName System.Net.Security.SslStream -ArgumentList @($TcpStream, $true, $Callback)
        try {

            $SslStream.AuthenticateAsClient($domain)
            $certificate = $SslStream.RemoteCertificate

        }
        finally {
            $SslStream.Dispose()
        }

    }
    finally {
        $TcpClient.Dispose()
    }

    if ($certificate) {
        if ($certificate -isnot [System.Security.Cryptography.X509Certificates.X509Certificate2]) {
            $certificate = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList $certificate
        }
    }

    return $certificate
}