Recheck the URL thrice for web exception cases powershell - powershell

$status_wait =1
Do{
$https_request= invoke_webrequest -uri "https:\\"
$https_status = [int]http_request.statuscode
If($($_.categoryinfo.reason) -eq "webexception") {
Start-sleep -secounds 30
$status_wait+=1
}
Elseif($http_status -eq 200) {
Write-host "URL is up"
}
}Untill (status_wait -eq 3)

You are looking for something like this ?
#Remove-Variable * -ErrorAction SilentlyContinue
$url = "www.yourtargetsite.com"
function try-webrequest($url){
try{
return (Invoke-WebRequest -uri $url -ErrorAction Stop)
}catch{
return $_
}
}
$wr = try-webrequest $url
if ($wr.statuscode -ne 200){
If($wr.categoryinfo.reason -eq "webexception"){
$count = 1
while($count -lt 4){
"Retrying...$count"
$count++
if ((try-webrequest $url).statuscode -eq 200){break}
sleep -seconds 30
}
}else{
#In case you want to handle any other exception
"The link is down NOT due to webexception"
}
}else{
"The link's up"
}
PS: I have just started answering on stackoverflow. I'd love to hear your feedback.

Related

Parsing Function return in PowerShell Script

I am trying to parse the response from one of the functions in PowerShell Script and based on the response I have to make some decision.
The function is returning JSON object successfully but not able to parse the response from the function. I need to check if validnodes count is 0 or not.
[String[]] $NodeList = 'a1572dev00e001','a1572dev00q001'
$Response = Get-Nodes
Write-Output "Response $Response"
$JSRes = $Response | ConvertFrom-Json
Write-Output "Parsing response $JSRes"
#$Result = "success"
if($JSRes.ValidNodes.Count -gt 0)
{
Write-Output "$JSRes.ValidNodes"
$Result = "success"
}
else
{
Write-Output "All nodes are Invalid"
Write-Output "Invalid Nodes: $JSRes.Invalid"
$Result = "failed"
$ErrorMessage = "All nodes are Invalid"
}
Write-Output $Result
#Function
function Get-Nodes
{
$ValidNodes=#()
$InvalidNodes=#()
foreach($Node in $NodeList)
{
if(Get-ADComputer -filter {Name -eq $Node})
{
$ValidNodes +=$Node
}
else
{
$InvalidNodes +=$Node
}
}
$JRes = #{"ValidNodes"=$ValidNodes;"Invalid"=$InvalidNodes} | ConvertTo-Json -Compress
Write-Output $JRes
return $JRes
}
Output:
Response {"ValidNodes":["a1572dev00e001","a1572dev00q001"],"Invalid":[]} {"ValidNodes":["
a1572dev00e001","a1572dev00q001"],"Invalid":[]}
Parsing response
All nodes are Invalid
Invalid Nodes:
failed
One issue is you are outputting the $Jres twice.
Write-Output $JRes
return $JRes
These effectively do the exact same thing. Next, you're using ConvertFrom-String when it seems you should be using ConvertFrom-Json
$JSON = $Response | ConvertFrom-String
Finally, you're trying to output $ValidNodes and $InvalidNodes that only exist in your function. Change these to $JSON.ValidNodes and $JSON.InvalidNodes
One more suggestion is to parameterize the Nodelist so you can just pass the nodes to the function.
#Function
function Get-Nodes
{
Param([string[]]$nodelist)
$ValidNodes=#()
$InvalidNodes=#()
foreach($Node in $NodeList)
{
if(Get-ADComputer -filter {Name -eq $Node})
{
$ValidNodes +=$Node
}
else
{
$InvalidNodes +=$Node
}
}
$JRes = #{"ValidNodes"=$ValidNodes;"Invalid"=$InvalidNodes} | ConvertTo-Json -Compress
$JRes
}
$Response = Get-Nodes a1572dev00e001,a1572dev00q001
Write-Output "Response $Response"
$JSON = $Response | ConvertFrom-Json
Write-Output "Parsing response $JSON"
if($JSON.ValidNodes.Count -gt 0)
{
Write-Output "Valid Nodes: $($JSON.ValidNodes)"
$Result = "success"
}
else
{
Write-Output "All nodes are Invalid"
Write-Output "Invalid Nodes: $($JSON.InValidNodes)"
$Result = "failed"
$ErrorMessage = "All nodes are Invalid"
}
Write-Output $Result

Simple Powershell IIS Site Verification

part of our checkout process after patching is checking webservers. I'd like create a for each loop that runs the following script looking for 200 or 401 and pops terminal message a "site ok message" with the site name. If it gets anything else, I'd like an error message that gives the site name as well.
I know 401 response isn't really ok, but some of the sites I manage I don't have rights to view. (Hence is why I wanted to go this route, I never know what "looks right" on a visual check. Here is what I have so far.
$HTTP_Request = [System.Net.WebRequest]::Create('https://someinternalserver.com')
$HTTP_Response = $HTTP_Request.GetResponse()
$HTTP_Status = [int]$HTTP_Response.StatusCode
If ($HTTP_Status -eq 200) {
Write-output $http_request | Select-Object RequestUri
Write-Host "SITE OK! 200"
If ($HTTP_Status -eq 401) {
Write-output $http_request | Select-Object RequestUri
Write_Host "SITE OK! 401"}
}
Else {
Write-output $http_request | Select-Object RequestUri
Write-Host "SITE DEAD"}
If ($HTTP_Response -eq $null) { }
Else { $HTTP_Response.Close() }
When I run the command, I am getting error messages that I don't really care about on line 5. How do I supress them? Ideally I am trying to make it look clean as I can so we can just watch info scroll by and note any server that is something other than 200 or 401results
...this was working too sort of but now I seem to have broken it to the point where everything comes back as SITE DEAD.
You could add a try/catch.
$HTTP_Request = [System.Net.WebRequest]::Create('http://localhost:8080')
$HTTP_Response
$HTTP_Status
try {
$HTTP_Response = $HTTP_Request.GetResponse()
$HTTP_Status = [int]$HTTP_Response.StatusCode
}
catch {
}
If ($HTTP_Status -eq 200) {
Write-output $http_request | Select-Object RequestUri
Write-Host "SITE OK! 200"
If ($HTTP_Status -eq 401) {
Write-output $http_request | Select-Object RequestUri
Write_Host "SITE OK! 401"
}
}
Else {
Write-output $http_request | Select-Object RequestUri
Write-Host "SITE DEAD"
}
If ($HTTP_Response -eq $null) { }
Else { $HTTP_Response.Close() }
If you want to hit some URLs in a loop, you can do something like this:
param(
$urls = #('http://localhost:8080', 'https://google.com')
)
Clear-Host
foreach ($url in $urls) {
$HTTP_Request = [System.Net.WebRequest]::Create($url)
$HTTP_Response
$HTTP_Status
try {
$HTTP_Response = $HTTP_Request.GetResponse()
$HTTP_Status = [int]$HTTP_Response.StatusCode
}
catch {
}
If ($HTTP_Status -eq 200) {
Write-Host ($http_request | Select-Object RequestUri).RequestUri
Write-Host "SITE OK! 200 `n"
}
ElseIf ($HTTP_Status -eq 401) {
Write-Host ($http_request | Select-Object RequestUri).RequestUri
Write_Host "SITE OK! 401 `n"
}
Else {
Write-Host ($http_request | Select-Object RequestUri).RequestUri
Write-Host "SITE DEAD `n"
}
# $null should be on the left: https://evotec.xyz/the-curious-case-of-null-should-be-on-the-left-side-of-equality-comparisons-psscriptanalyzer/
If ($null -eq $HTTP_Response) { }
Else { $HTTP_Response.Close() }
}

Does "if any" in a foreach loop exist?

I am trying to comb through a WebServer's app pools to detect the HTTP Response codes of each app. I am using a foreach loop to check for the response to be 200, but if one of the responses is something other than a 200 I need the foreach loop to continue and check all the other app pools.
$appPool = Get-WebApplication
foreach ($a in $appPool) {
$app = $a.Attributes[0].Value;
$url = "http://localhost$app/apitest/index"
$HTTP_Request = [System.Net.WebRequest]::Create($url)
$HTTP_Response = try{
$HTTP_Request.GetResponse()
} catch {
$exceptionMessage = $_.Exception.Message
$exceptionItem = $app
}
$HTTP_Status = [int]$HTTP_Response.StatusCode
if ($HTTP_Status -eq 200) {
$errorcode = 0
} elseif ($HTTP_Status -ne 200) {
$errorcode = 1
} else {
$errorcode = 2
}
}
I'm finding that it doesn't matter what any app pools return because the loop just exits with whatever the last app returns. If app 3 returns a 503 but the last app returns a 200, then the foreach loop returns a 200 and exits with $errorcode = 0.
How do I change this code to check all app pools but exit with a different error code if an app in the middle does not have a 200 status code?
One way you can do this is to embrace the returned booleans as a list and just check that the list contains the value. For example:
$results = foreach($n in 1..10) {
$n -eq 5
}
if ($results -contains $true) {
Write-Host "There was a 5"
}
For your example, I guess:
$appPool = get-webapplication
$results = foreach($a in $appPool) {
$app = $a.Attributes[0].Value;
$url = "http://localhost$app/apitest/index"
$HTTP_Request = [System.Net.WebRequest]::Create($url)
$HTTP_Response = try {
$HTTP_Request.GetResponse()
} catch {
$exceptionMessage = $_.Exception.Message
$exceptionItem = $app
}
[int]$HTTP_Response.StatusCode -ne 200
}
if ($results -contains $true) {
$errorcode = 1
} else {
$errorcode = 0
}
I didn't want to clutter the example, but I might actually just do:
$errorcode = $results -contains $true -as [int]
I would pre-set $errorcode before entering the loop, and change its value only when the status code for a request is not 200.
$errorcode = 0
foreach ($a in $appPool) {
...
if ($HTTP_Response.StatusCode.value__ -ne 200) {
$errorcode = 1
}
}

Powershell document.getElementbyID - doesn't pull right information

I got a little powershell problem troubling me for quite a while now.
Im trying to get information from a RSS site. I download the XML and go through it. I just want certain stuff from it. That for I use .document.getElementByID().outerText
The problem is that somehow it pulls the first information correctly but after that everything fails he just picks random text or just keeps the one text from the beginning without refreshing the variable. Also Powershell ISE says "You cannot call a method on a null-valued expression." randomly.
Here is my code:
<#
AUTHOR: KOCH,MICHAEL [GRE-IT]
DESCRIPTION: RSS READER
DATE: 28.06.17
DATE LAST WRITTEN: 19.07.17
LAST CHANGE:
#>
$debug = 1 #DEBUG
$receiver="A#MailAdress.com"
$sender="A#MailAdress.com"
$smtp="A.SMTP.SERVER"
$encoding = [System.Text.Encoding]::UTF8
$path_config = "C:\RSS\Zoll\config.txt"
$output = "C:\RSS\Zoll\meldung.html"
$output_edit_path = "C:\RSS\Zoll\meldung_edit.html"
$nmbr=0
$count=0
Invoke-WebRequest -Uri 'http://www.zoll.de/SiteGlobals/Functions/RSSFeed/DE/RSSNewsfeed/RSSZollImFokus.xml' -OutFile C:\RSS\Zoll\meldungen.xml -ErrorAction Stop
[xml]$content = Get-Content C:\RSS\Zoll\meldungen.xml
$feed = $content.rss.channel
$tag = #()
if($lines=Get-Content $path_config | Measure-Object -Line) #gets the number of lines
{
while($count -ne $lines.Lines)
{
if($entrys=(Get-Content $path_config)[$nmbr]) #gets the entrys from config.txt and goes through line by line
{
$entrys >> $tag[$nmbr]
if ($debug -eq 1)
{
Write-Output "$tag[$nmbr]"
Write-Output "$entrys"
Write-Output "$count"
}
}
$count++
$nmbr++ #jumps into the next line
}
}
$ie = New-Object -ComObject "InternetExplorer.Application"
Foreach($msg in $feed.Item)
{
$link = ($msg.link)
$subject = ($msg.title)
$ie.navigate("$link")
#$return = Invoke-WebRequest -Uri $link -OutFile "C:\RSS\Zoll\link.html"
$return = $ie.document
$innertext = $return.documentElement.document.getElementById("main").outerText
$body = $innertext#.Replace('Ä', 'Ä')
<#
$body = $innertext.Replace('ä', 'ä')
$body = $innertext.Replace('Ö', 'Ö')
$body = $innertext.Replace('ö', 'ö')
$body = $innertext.Replace('Ü', 'Ü')
$body = $innertext.Replace('ü', 'ü')
$body = $innertext.Replace('ß', 'ß')
#>
if ($debug -eq 1)
{
Write-Output "Subject $subject"
Write-Output "Tag $tag"
Write-Output "Link $link"
Write-Output $body
#exit
}
if($link -match "Zigaretten") #searchs in the <link> for the string "Zigaretten"
{
if($subject -match $tag) #searches for the specified tag in config.txt !!! only one argument per line !!!
{
if($mail = Send-MailMessage -From "$sender" -To "$receiver" -Subject "Zoll Meldung: $subject" -Body "$body" -SmtpServer "$smtp" -BodyAsHtml -encoding $encoding)
{
if($debug -eq 1)
{
Write-Output "$tag"
Write-Output "Send. Tag = $tag"
}
Write-Output "Send."
}
}
}
else
{
Write-Host "Empty."
}
}
$ie.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie)
Remove-Variable ie
Added a wait if busy loop to make sure IE loads the full html document. Thats the solution of the problem ! :)
<#
AUTHOR: KOCH,MICHAEL [GRE-IT]
DESCRIPTION: RSS READER
DATE: 28.06.17
DATE LAST WRITTEN: 20.07.17
LAST CHANGE: ADDED WAIT IF BUSY !
#>
$debug = 0 #DEBUG
$receiver="A#MailAdress.de"
$sender="A#MailAdress.de"
$smtp="A.SMTP.SERVER"
$encoding = [System.Text.Encoding]::UTF8
$path_config = "C:\RSS\Zoll\config.txt"
$output = "C:\RSS\Zoll\meldung.html"
$output_edit_path = "C:\RSS\Zoll\meldung_edit.html"
$nmbr=0
$count=0
Invoke-WebRequest -Uri 'http://www.zoll.de/SiteGlobals/Functions/RSSFeed/DE/RSSNewsfeed/RSSZollImFokus.xml' -OutFile C:\RSS\Zoll\meldungen.xml -ErrorAction Stop
[xml]$content = Get-Content C:\RSS\Zoll\meldungen.xml
$feed = $content.rss.channel
$tag = #()
if($lines=Get-Content $path_config | Measure-Object -Line) #gets the number of lines
{
while($count -ne $lines.Lines)
{
if($entrys=(Get-Content $path_config)[$nmbr]) #gets the entrys from config.txt and goes through line by line
{
$entrys >> $tag[$nmbr]
if ($debug -eq 1)
{
Write-Output "$tag[$nmbr]"
Write-Output "$entrys"
Write-Output "$count"
}
}
$count++
$nmbr++ #jumps into the next line
}
}
$ie = New-Object -ComObject InternetExplorer.Application #creates new ComObject IE
Foreach($msg in $feed.Item)
{
$link = ($msg.link)
$subject = ($msg.title)
if ($debug -eq 1)
{
$ie.visible = $true
}
$ie.navigate("$link") #navigate with Internetexplorer to the website
while ($ie.busy -and $ie.ReadyState -ne 4){ sleep -Milliseconds 200 } # if getting the website from IE.navigate is still .busy wait 200 milliseconds
$return = $ie.document
$innertext = $return.documentelement.document.IHTMLDocument3_getElementById("main").outerText #gets the outer text from the div with the element ID "main"
while ($innertext.busy -and $innertext.ReadyState -ne 4){ sleep -Milliseconds 200 } # if getting Text is .busy wait 200 milliseconds
$body = $innertext
if ($debug -eq 1)
{
Write-Output "Subject $subject"
Write-Output "Tag $tag"
Write-Output "Link $link"
Write-Output "INNERTEXT $innertext"
Write-Output "BODY $body"
#exit
}
if($link -match "Zigaretten") #searchs in the <link> for the string "Zigaretten"
{
if($subject -match $tag) #searches for the specified tag in config.txt !!! only one argument per line !!!
{
if($mail = Send-MailMessage -From "$sender" -To "$receiver" -Subject "Zoll Meldung: $subject" -Body "$body" -SmtpServer "$smtp" -BodyAsHtml -encoding $encoding)
{
Write-Output "Send."
}
}
}
else
{
Write-Host "Empty."
}
}
$ie.Quit() #----|
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie) # ---> Quits the Internet Explorer Session otherwise there are to many IE.exe open and no more ID's left
Remove-Variable ie

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

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
}
}
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> </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
Put the whole code you've wrote in a script-block, in order to do so, just add at the beginning this code:
$sb = {
and at the bottom of your code add:
}
Once you have this script-block, you can run this script on the servers remotely using these commands:
$cred = Get-Credential
$servers = get-content D:\Certificate.txt
Invoke-Command -Credential $cred -ComputerName $servers -ScriptBlock $SB
Hope it helped!
Your code snippets are helpful but they will throw an error on HTTP errors -- that is the nature of the underlying .NET HttpWebRequest object to panic on everything that's not code 200.
So, if you're testing, say, API endpoints that only accept POST HTTP verb, your script will fail as it will get a 405 error message. Alternatively, if your URL returns HTTP 404, your script will fail as well. Luckily, you can catch layer 7 errors and capture the required info anyway just patch your code in this manner:
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
}
}
Taken mostly from https://gist.github.com/jstangroome/5945820, although with some changes. The following will obtain the certificate. $certificate.NotBefore and $certificate.NotAfter will then need to be checked.
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
}