Skip to content

Instantly share code, notes, and snippets.

@Hashbrown777
Last active April 12, 2021 04:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hashbrown777/69daf94bb291fe2dc5115e0a704f64b4 to your computer and use it in GitHub Desktop.
Save Hashbrown777/69daf94bb291fe2dc5115e0a704f64b4 to your computer and use it in GitHub Desktop.
Give a detailed report on the ping and tcp status of servers.
#Requires -Version 5.1
. ./async.ps1
. ./asynclet.ps1
. ./clone.ps1
. ./colors.ps1
. ./htmlTable.ps1
. ./ServerCheck.ps1
. ./windows.ps1
. ./wrap.ps1
$config='[
{name:"UPS", address:"10.<<REDACTED>>.28", services:[{port:"http"}]},
{name:"proxmox", address:"10.<<REDACTED>>.24", services:[
{port:[22,"ssh"]},
{port:[8006,"https"]}
]},
{name:"Murphy" , address:"10.<<REDACTED>>.182", services:[{name:"Meetings", port:[7250,"Miracast"]}] ,ignore:true},
{name:"Oliver" , address:"10.<<REDACTED>>.166", services:[{name:"Meetings", port:[7250,"Miracast"]}] ,ignore:true},
{name:"Rossiter", address:"10.<<REDACTED>>.87" , services:[{name:"Meetings", port:[7250,"Miracast"]}] ,ignore:true},
//? https://lyncadmin.nswgov.ucfx.com/cscp/
{name:"skype", services:[{address:"sfbint.<<REDACTED>>.ucfx.com" , port : "https"}]},
{name:"skype", services:[{address:"sfbext.<<REDACTED>>.ucfx.com" , port : "https"}]},
{name:"skype", services:[{address:"sfbpool.<<REDACTED>>.ucfx.com", ports:[[8060,"sip"], [8061,"sip"]]}]},
{name:"CP1", address:"10.<<REDACTED>>.11", services:[{ports:[["http","admin"], ["https","admin"], [445,"smb"], [139,"NetBIOS"]]}]},
{name:"CP3", address:"10.<<REDACTED>>.12", services:[{port : ["http","admin"]}]},
{name:"CP5", address:"10.<<REDACTED>>.13", services:[{port : ["http","admin"]}]},
{name:"CP9", address:"10.<<REDACTED>>.14", services:[{ports:[["http","admin"], ["https","admin"]]}]},
{name:"PUBLIC", services:[
{address: "beta.<<REDACTED>>.au", ports:["https"]},
{address: "www.<<REDACTED>>.au", ports:["https", ["http","redirect"]]},
{address: "<<REDACTED>>.au", ports:["https", ["http","redirect"]]},
{address:"www.gazette.<<REDACTED>>.au", ports:["https", ["http","redirect"]]},
{address: "gazette.<<REDACTED>>.au", ports:["https", ["http","redirect"]]}
]},
{name:"DC", address:"dcdb", services:[
{ports:[[22,"ssh"], [445,"smb"], [139,"NetBIOS"]]},
//teratext
{name:"dirs" , ports:[[7500,"http"], [7501,"sal"]]},
{name:"boots", port : [7502,"http"]},
{name:"sls" , ports:[[7504,"http"], [7505,"sal"]]},
{name:"ddi" , port : [7506,"http"]},
{name:"adv" , port : [7507,"http"]},
//applications
{name:"XXX website" , port : [7001,"http"]},
{name:"YYY website" , port : [7002,"http"]},
{name:"ZZZ intranet" , ports:[[8082,"http"], [7514,"teratext"]]},
{name:"AAA intranet" , ports:[[8088,"http"], [7513,"teratext"]]},
{name:"BBB Live", ports:[
[8080,"http"],
[6502,"http","SOAP"],
[8081,"http","WebDAV"],
[6504,"http","CDMS"],
[7511,"teratext"],
]},
{name:"CCC", ports:[
[8084,"http"],
[7512,"teratext"],
[7509,"http","Replication Server"]
]},
]},
{name:"drWEB", address:"drweb", services:[
{port:[22,"ssh"]},
//teratext
{name:"dirs" , ports:[[7500,"http"], [7501,"sal"]]},
{name:"boots", port : [7502,"http"]},
{name:"sls" , ports:[[7504,"http"], [7505,"sal"]]},
{name:"ddi" , port : [7506,"http"]},
{name:"adv" , port : [7507,"http"]},
//applications
{name:"XXX website" , port : [7001,"http"]},
{name:"YYY website" , port : [7002,"http"]},
{name:"AAA website" , ports:[[8082,"http"], [7514,"teratext"]]},
{name:"BBB website" , ports:["http", [7513,"teratext"]]},
// {name:"test website", ports:[[8887,"http"], [8889,"https"]]},
// ]},
],
//TODO fix this
ignore:true
},
{name:"DEV", address:"10.<<REDACTED>>.110", services:[
{port:[22,"ssh"]},
{name:"TeraText", ports:[
[7609,"https","admin"],
[7601,"sal","dirs"],
[7602,"https","boots"],
[7603,"ws","monitor"],
[7605,"sal","sls"],
[7610,"teratext","manuals"],
[7612,"https","rs",false],
]},
{name:"XXX", ports:[
[8080,"https"],
[7070,"teratext"],
[6058,"https","SOAP"],
[6059,"https","WebDAV"],
[6060,"https","CDMS"],
]},
{name:"YYY", ports:[
[8081,"https"],
[7071,"teratext"],
[6061,"https","RS"]
]},
{name:"AAA intranet", ports:[
[8082,"https"],
[7072,"teratext"]
]},
{name:"BBB intranet", ports:[
[8083,"https"],
[7073,"teratext"]
]},
{name:"AAA website", ports:[
[8084,"https"],
[7074,"teratext"]
]},
{name:"BBB website", ports:[
[8085,"https"],
[7075,"teratext"]
]},
// {name:"PCO website", port:[7001,"https"]},
// {name:"PCC website", port:[7002,"https"]},
]},
//DNS testing
//TODO get them to finally get this good
// {address:"ABC"},
// {address:"ABC.X.Y.Z.au"},
// {address:"ABC.x.Y.Z.au"},
// {address:"DEF"},
// {address:"DEF.X.Y.Z.au"},
// {address:"DEF.x.Y.Z.au"},
]'
$_TABLE_TITLE='The powershell window will show progress. You can email or run again by making a selection'
$config=$config `
-replace '(?<=^|\n)((?:[^\n"\\/]|\\.|"(?:[^\n"\\]|\\.)*")*)//[^\r\n]*(?=\r|\n|$)' ,'$1' <#allow comments in the JSON#>`
-replace '(?<=^|\n)((?:[^\n"\\,]|\\.|"(?:[^\n"\\]|\\.)*"|,\s*(?!\s|}|]))*),(?=\s*[}\]])','$1' <#allow trailing commas in the JSON#>`
| ConvertFrom-Json `
| Write-Output -NoEnumerate <#bug in pwsh, may need removing if we update to PS6#>`
| TransformConfig
#main loop
do {
$results=[Asynclet]::new('Out-GridView', @{PassThru=$True; Title=$_TABLE_TITLE})
$timer=[system.diagnostics.stopwatch]::StartNew()
$config `
| Clone `
| Async `
-Name 'Testing Services' `
-Expected $config.Count `
-Func $Test `
-PassThru `
-AsJob `
-Parameters @{
Proxy=[System.Net.WebRequest]::GetSystemWebProxy();
SecurityProtocol=`
[Net.SecurityProtocolType]::Tls -bor `
[Net.SecurityProtocolType]::Tls11 -bor `
[Net.SecurityProtocolType]::Tls12 -bor `
[Net.SecurityProtocolType]::Tls13 -bor `
[Net.SecurityProtocolType]::Ssl3
;
} `
| Analyse `
| Present -Results $results `
| ResetScreen -Before -After `
| Print `
| Sort-Object -Property machine,up,service,address,proto `
| Format-Table -Wrap -AutoSize -GroupBy machine -Property up,service,address,port,proto,info
("
Completed in
$([math]::Floor($timer.Elapsed.TotalMinutes))
:
$($timer.Elapsed.Seconds -replace '^(.)$','0$1')
" -replace '\r?\n\t*','')
Title 'You may now make a selection and close the table'
$_TITLE
BringToFront $_TABLE_TITLE
$results=`
try {
$results.done()
}
catch {
@()
}
$NULL | ResetScreen
BringToFront $_TITLE
Title "$failed failures. $($results.Count) items selected"
#finish up with a total time and wait for the user to close
Switch -exact ($Host.UI.PromptForChoice(
$_TITLE,
'What would you like to do now?',
(&{
if ($results.Count) {
'&Email'
}
'&Run again'
'E&xit'
}),
#make sure exit is selected
@(2,1)[!$results.Count]
)) {
@(1,0)[!$results.Count] {
$continue=$True
}
@(0,-1)[!$results.Count] {
$outlook=New-Object -ComObject Outlook.Application
$email=$outlook.CreateItem(<#olMailItem#>0)
$email.BodyFormat=<#olFormatHTML#>2
$email.HTMLBody=$results | Html | Concat
$email.Display()
}
}
} until ($continue=!$continue)
'Thank you :)'
#recognised protocols
$protocols=@{
http =80 ;
https=443;
}
$names=@{
teratext='Content Server';
smb ='Network share' ;
ssh ='Remote shell' ;
rdp ='Remote desktop';
}
$icons=@{
'Content Server'=0x1F4BD;
'NetBIOS' =0x1F5A7;
'Network share' =0x1F4C2;
'Remote shell' =0x1F4BB;
'Remote desktop'=0x1F5B3;
}
Filter TransformConfig {
#launder the input so its persistent
$machine=$_ | ConvertTo-Json -Depth 32 -Compress | ConvertFrom-Json
class Machine {
$machine=$machine.name
$address=$machine.address
$reverse=$machine.reverse
$ignore=$machine.ignore -or $machine.disable
$service='ping'
$proto='icmp'
}
#if we have a machine address, feed it on
if ($machine.address) {
[Machine]::new()
}
#if we have services
if (!$machine.services) {
return
}
$machine.services `
| %{
$service=$_
class Service : Machine {
$address=($machine.address,$service.address -match '.')[0]
$service=$service.name
$reverse=$service.reverse -or $machine.reverse
$ignore=$service.ignore -or $machine.disable
$port
Service($name, $port, $proto, $reverse, $ignore) {
$this.port=$port
$this.proto=$proto
$this.service=@($this.service,$name) -match '.' -join ' - '
$this.reverse=$this.reverse -or $reverse
$this.ignore=$this.ignore -or $ignore
}
}
#process them and feed them on
&{
if ($service.port) {
,@($service.port)
}
else {
$service.ports
}
} `
| &{
Begin { $start=$True }
Process {
$ignore=$_ | ?{ $_ -eq $NULL } | %{ $True } | Select-Object -First 1
$_=$_ | ?{ $_ -ne $NULL }
$proto =$_ | ?{ $protocols[$_] } | Select-Object -First 1
$port =$_ | ?{ $_ -is [int] } | Select-Object -First 1
$name =$_ | ?{ $_ -is [string] -and $_ -ne $proto } | Select-Object -Last 1
$reverse=$_ | ?{ $_ -is [bool] } | %{ !$_ } | Select-Object -First 1
if ($port) {}
elseif ($proto) {
$port=$protocols[$proto]
}
else {
Write-Error 'Unknown protocol' -TargetObject ($_ | ConvertTo-Json -Compress)
return
}
$name=if ($start -and $service.name -and !$name) {
$start=$False
''
}
elseif ($name -or $proto) {
if (!$name) {
$name=$proto
}
if ($names[$name]) {
$names[$name]
}
else {
$name
}
}
else {
$port
}
[Service]::new($name, $port, ('tcp','ssl')[$proto -eq 'https'], $reverse, $ignore)
if ($proto) {
[Service]::new($name, $port, $proto, $reverse, $ignore)
}
}
}
}
}
$tcpTest={ Param($address, $port, $proxy, $SecurityProtocol)
$socket=New-Object System.Net.Sockets.TcpClient
try {
$result=$socket.BeginConnect($address, $port, $NULL, $NULL)
if (!$result.AsyncWaitHandle.WaitOne(5000, $False)) {
throw [System.Exception]::new('Connection Timeout')
}
$socket.EndConnect($result) | Out-Null
if ($SecurityProtocol) {
$ssl=[System.Net.Security.SslStream]::new(
$socket.GetStream()
)
$result=$ssl.BeginAuthenticateAsClient(
$address,
(New-object System.Security.Cryptography.X509Certificates.X509CertificateCollection),
$x.SecurityProtocol,
$NULL,
$NULL
)
if (!$result.AsyncWaitHandle.WaitOne(5000, $False)) {
throw [System.Exception]::new('Connection Timeout')
}
$ssl.EndAuthenticateAsClient($result)
#$ssl.IsEncrypted -and $ssl.IsAuthenticated
$True
}
else {
$socket.Connected
}
if ($proxy) {
$Input `
| Add-Member `
-MemberType NoteProperty `
-Name info `
-Value 'Test succeeded without proxy'
}
}
catch {
if ($proxy -and !$SecurityProtocol) {
$Input `
| Add-Member `
-MemberType NoteProperty `
-Force `
-Name ignore `
-Value $True
[System.Exception]::new(
'Unable to test raw TCP, HTTP proxy cannot permit it'
)
}
else {
$_.Exception
}
}
finally {
$socket.Close()
}
}
$httpsSync={ Param($request, $SecurityProtocol)
if ($SecurityProtocol) {
[Net.ServicePointManager]::SecurityProtocol=$x.SecurityProtocol
}
$request.GetResponse()
}
$Test={ Param([parameter(Position=0)]$x)
Switch -regex ($x.input.proto) {
'^icmp$' {
$x.job=Test-Connection `
-ComputerName $x.input.address `
-Count 1 `
-ThrottleLimit 1 `
-AsJob
}
'^(tcp|ssl)$' {
$x.job=$tcpTest
$x.params=@{
address=$x.input.address
port=$x.input.port
SecurityProtocol=($NULL,$x.args.SecurityProtocol)[$x.input.proto -eq 'ssl']
proxy=$x.params.Proxy
}
}
'^(http|https)$' {
# if ($x.args.Expect100Continue) {
# [Net.ServicePointManager]::Expect100Continue=$x.args.Expect100Continue
# }
$uri=[System.UriBuilder]::new(
($x.input.proto -replace '^tcp$','http'),
$x.input.address,
$x.input.port
).Uri
$request=[System.Net.HttpWebRequest]::Create($uri)
if ($x.params.Credentials) {
$request.UseDefaultCredentials=$True
$request.Credentials=[System.Net.CredentialCache]::DefaultCredentials
}
if ($x.params.Proxy) {
$proxy=[System.Net.WebProxy]::new((
$x.params.Proxy.GetProxy($uri) -replace '(?<=^http)s(?=:)',''
))
if ($proxy.Address.Host -ne $uri.Host) {
$x.input `
| Add-Member `
-MemberType NoteProperty `
-Name proxy `
-Value $proxy.Address.AbsoluteUri
$request.Proxy=$proxy
$request.Proxy.Credentials=[System.Net.CredentialCache]::DefaultCredentials
}
}
$request.Timeout=2000
$request.Method='HEAD'
$request.KeepAlive=$False
$request.AllowAutoRedirect=$False
if ($x.input.proto -eq 'http') {
$x.job=$request.GetResponseAsync()
}
else {
#maybe we do want to test this for MITM attacks?
#dont bother assessing the certificates
$request.ServerCertificateValidationCallback+=[System.Net.Security.RemoteCertificateValidationCallback]{
param(
[object]$sender,
[System.Security.Cryptography.X509Certificates.X509Certificate]$certificate,
[System.Security.Cryptography.X509Certificates.X509Chain]$chain,
[System.Net.Security.SslPolicyErrors]$sslPolicyErrors
)
return $True
}
$x.job=$httpsSync
$x.params=@{
request=$request
SecurityProtocol=$x.args.SecurityProtocol
}
}
}
}
}
Function Analyse {
Begin { $script:failed=0 }
Process {
$result=$_.input
'up','good','info' | %{
if (!$result.($_)) {
$result | Add-Member -MemberType NoteProperty -Name $_ -Value $NULL
}
}
$response=$_.job
#[System.Net.WebException]
if ($response.Response -is [System.Net.HttpWebResponse]) {
$response=$response.Response
}
elseif ($response.InnerException) {
$response=$response.InnerException
}
$result.up=Switch -regex ($result.proto) {
#[Exception]
{$response -is [Exception]} {
$False
$result.info=$response.Message
break
}
#[Win32_PingStatus]
'^icmp$' {
$up=$False
$result.info=Switch -exact ($response.StatusCode) {
0 { $up=$True; }
11001 { 'Buffer Too Small' }
11002 { 'Destination Net Unreachable' }
11003 { 'Destination Host Unreachable' }
11004 { 'Destination Protocol Unreachable' }
11005 { 'Destination Port Unreachable' }
11006 { 'No Resources' }
11007 { 'Bad Option' }
11008 { 'Hardware Error' }
11009 { 'Packet Too Big' }
11010 { 'Request Timed Out' }
11011 { 'Bad Request' }
11012 { 'Bad Route' }
11013 { 'TimeToLive Expired Transit' }
11014 { 'TimeToLive Expired Reassembly' }
11015 { 'Parameter Problem' }
11016 { 'Source Quench' }
11017 { 'Option Too Big' }
11018 { 'Bad Destination' }
11032 { 'Negotiating IPSEC' }
11050 { 'General Failure' }
}
$up
}
#[bool]
'^tcp$' {
$response
}
#[System.Net.HttpWebResponse]
'^(http|https)$' {
$up=[int]$response.StatusCode
$result.info="$up`: $($response.StatusDescription)"
$result `
| Add-Member `
-MemberType NoteProperty `
-Name url `
-Value $response.ResponseUri
#okay
if ($up -ge 200 -and $up -le 299) {
if ($up -eq 200) {
$result.info='200'
}
}
#redirect
elseif ($up -ge 300 -and $up -le 399) {
$to=$response.GetResponseHeader('Location')
$result.info="$up`: $to"
if (
#it's not relative
$to -match '^[^?#/]*:' -and `
!(#and going to a different domain
$to -replace '^[^?#/:]*:(?://|)([^/:]*)(?:[/:].*|)$','$1'
).EndsWith($result.address)
) {
#fail
$up=$False
}
}
#not needing login
elseif ($up -ne 401 -and $up -ne 403) {
#fail
$up=$False
}
if ($response) {
$response.Close()
}
!!$up
}
}
#broken depends on whether the service is -meant- to be up
$result.good=(!$result.up) -ne (!$result.reverse)
if (!($result.ignore -or $result.good)) {
++$script:failed
Title ("$failed FAILED! " + (
($result `
| Get-Member -Name machine,name,address,port,proto `
| %{$result.($_.Name)} `
) -join ' '
))
}
$result
}
}
Function Print {
Begin {
$print=@('up','machine','service','address','port','proto','info')
}
Process {
$x=$_ | Select-Object $print
$colour=@($style.bf.red,$style.bf.black)[$_.good -or $_.ignore]
$x.up=`
&{
if ($_.ignore) {
$colour
}
#colour the tick
elseif ($_.good) {
$style.bf.green
}
else {
$style.bg.black;$style.bf.red;$style.underline
}
#tick character (actually a sqrt because PS doesnt have good unicode yet)
@(0x221A, 'DOWN')[!$x.up]
$style.nounderline
} `
| Unicode
# $x.address=Hyperlink -Link $_.url -Text $x.address
$x.machine=$style.bf.magenta,$x.machine -join ''
$x.service=$style.bf.cyan,$x.service -join ''
$x.address=$style.bf.white,$x.address -join ''
$x.proto=$style.underline,$color,$x.proto,$style.nounderline -join ''
$x.port=$colour,$x.port -join ''
$info=$style.bf.yellow,'{0}',$style.bg.default,$style.fg.default,$style.nounderline,$style.default -join ''
$x `
| Clone `
| %{
$_.machine=$_._,$_.machine,$_._ -join ''
$_.info=$info -f $_.info
$_
}
#output the result to the console as we go
$print `
| %{
$(switch -exact ($_) {
'info' {
if ($x.info -and !$x.good) {
(
Wrap `
-Length ($Host.UI.RawUI.WindowSize.Width - 10) `
($info -f $x.info) `
) -replace '^|\r?\n|$',"$([System.Environment]::NewLine)`t"
}
}
Default { $x.($_) }
}) -replace '^$',"`t"
} `
| Concat -Join "`t" `
| Write-Host
}
}
Function Present {
Param($Results)
Begin {
$machineIcons=@{
'CP1|CP3|CP5|CP9' =0x1F5A8;
'PUBLIC' =0x1F30E;
'skype' =0x1F4DE;
'DC' =0x1F5C4;
'DR' =0x1F5C3;
'TEST' =0x1F5B0;
'..WEB' =0x1F30F;
'proxmox' =0x1F5A5;
'UPS' =0x1F50C;
'Rossiter|Oliver|Murphy'=0x1F5D4;
}
$up=0x1F4A1 | Unicode
$show=@('good',$up,'machine','service','address','port','proto','info','proxy')
$hide=@('url')
}
Process {
if ($_.port) {
$_.port=[string]$_.port
}
$_.info=$_.info -replace '(?<=[:.])[^\S\r\n]+',[System.Environment]::NewLine
$_ | Clone
$_.info=Wrap -Length 55 $_.info
$_.good=`
$(
if ($_.ignore) {
''
}
elseif ($_.good) {
0x1F5F8
}
else {
0x274C
}
) `
| Unicode
$_ `
| Add-Member `
-MemberType NoteProperty `
-Name $up `
-Value (
$(
if ($_.up) {
0x25B5
$_.info=$_.info -replace '^(?=.)',(0x1F6C8,0xA0 | Unicode)
}
else {
0x25BE
$_.info=$_.info -replace '^(?=.)',(0x26A0,0xFE0F,0xA0 | Unicode)
}
) `
| Unicode
)
$machineIcons.GetEnumerator() `
| &{
Param([parameter(Position=0)]$x, [parameter(ValueFromPipeline=$True)]$y)
Process {
$x.machine=$x.machine -creplace "^(?=$($y.Key)$)",($y.Value,0xA0 | Unicode)
}
} $_
$icons.GetEnumerator() `
| &{
Param([parameter(Position=0)]$x, [parameter(ValueFromPipeline=$True)]$y)
Process {
$x.service=$x.service -replace "^(?:.* - |)$($y.Key)`$",($y.Value,0xA0,'$0' | Unicode)
}
} $_
$_.service=$_.service `
-replace '^redirect$',(0x21BA | Unicode) `
-replace '^ping$',(0x2911 | Unicode)
#pipe the result to the table generator
$Results.input((
$_ `
| Select-Object $show `
| Add-Member `
-MemberType NoteProperty `
-PassThru `
-Name _ `
-Value (
$_ `
| Select-Object $hide `
| Add-Member `
-MemberType ScriptMethod `
-Name ToString `
-Value {''} `
-Force `
-PassThru
)
))
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment