Functions/Test-ConnectionAsync.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
function Test-ConnectionAsync {
<#
.SYNOPSIS
    Performs a ping test asynchronously
.DESCRIPTION
    Performs a ping test asynchronously
.PARAMETER ComputerName
    List of computers to test connection. Aliased to 'CN', 'Server'
.PARAMETER Timeout
    Timeout in milliseconds. Default 2000 ms.
.PARAMETER TimeToLive
    Sets a time to live on ping request. Default 128.
.PARAMETER BufferSize
    How large you want the buffer to be. Valid range 32-65500, default of 32. If the buffer is 1501 bytes or greater then the ping can fragment.
.PARAMETER IncludeSource
    A switch determining if you want the source computer name to appear in the output
.PARAMETER Full
    A switch determining if full output appears
.NOTES
    Inspired by Test-ConnectionAsync by 'Boe Prox'
    https://gallery.technet.microsoft.com/scriptcenter/Asynchronous-Network-Ping-abdf01aa
    * fixed logic around processing pipeline
    * removed $Buffer parameter
    * added $BufferSize parameter and dynamically create $Buffer from $BufferSize
    * added $IncludeSource so that source computer would be included in output
    * added $Full so that default output is brief
    * changed datatype of .IPAddress to [version] so that it can be sorted properly
.OUTPUTS
    [pscustomobject] with output from Net.AsyncPingResult and optionally the source address
.EXAMPLE
    Test-ConnectionAsync -ComputerName server1,server2
 
    ComputerName IPAddress Result
    ------------ --------- ------
    server1 192.168.1.31 Success
    server2 192.168.1.41 Success
 
    Description
    -----------
    Performs asynchronous ping test against listed systems and lists brief output.
.EXAMPLE
    Test-ConnectionAsync -ComputerName server1,server2 -Full
 
    ComputerName IPAddress BufferSize Result ResponseTime
    ------------ --------- ---------- ------ ------------
    server1 192.168.1.31 32 Success 86
    server2 192.168.1.41 32 Success 79
 
    Description
    -----------
    Performs asynchronous ping test against listed systems and lists full output.
.EXAMPLE
    Test-ConnectionAsync -ComputerName server1,server2 -Full -BufferSize 1500
 
    ComputerName IPAddress BufferSize Result ResponseTime
    ------------ --------- ---------- ------ ------------
    server1 192.168.1.31 1500 Success 140
    server2 192.168.1.41 1500 Success 137
 
    Description
    -----------
    Performs asynchronous ping test against listed systems and lists full output with a buffersize of 1500 bytes.
#>


    #Requires -Version 3.0

    [OutputType('Net.AsyncPingResult')]
    [CmdletBinding(ConfirmImpact = 'None')]
    Param (
        [parameter(ValueFromPipeline, Position = 0)]
        [string[]] $ComputerName,

        [parameter()]
        [int] $Timeout = 2000,

        [parameter()]
        [Alias('Ttl')]
        [int] $TimeToLive = 128,

        [parameter()]
        [validaterange(32, 65500)]
        [int] $BufferSize = 32,

        [parameter()]
        [switch] $IncludeSource,

        [parameter()]
        [switch] $Full
    )

    begin {
        Write-Verbose -Message "Starting [$($MyInvocation.Mycommand)]"
        if ($IncludeSource) { $Source = $env:COMPUTERNAME }
        $Buffer = New-Object -TypeName System.Collections.ArrayList
        1..$BufferSize | ForEach-Object { $null = $Buffer.Add(([byte] [char] 'A')) }
        $PingOptions = New-Object -TypeName System.Net.NetworkInformation.PingOptions
        $PingOptions.Ttl = $TimeToLive
        If ($BufferSize -gt 1500) {
            $DontFragment = $false
        } else {
            $DontFragment = $true
        }
        Write-Verbose -Message "ComputerName [$($ComputerName -join ',')]"
        Write-Verbose -Message "BufferSize [$BufferSize]"
        Write-Verbose -Message "Timeout [$Timeout]"
        Write-Verbose -Message "TimeToLive [$TimeToLive]"
        Write-Verbose -Message "IncludeSource [$IncludeSource]"
        Write-Verbose -Message "Full [$Full]"
        Write-Verbose -Message "DontFragment [$DontFragment]"
        $PingOptions.DontFragment = $DontFragment
        $Computerlist = New-Object -TypeName System.Collections.ArrayList
    }

    process {
        foreach ($Computer in $ComputerName) {
            [void] $Computerlist.Add($Computer)
        }
    }

    end {
        $Task = foreach ($Computer in $ComputerList) {
            [pscustomobject] @{
                ComputerName = $Computer
                Task         = (New-Object -TypeName System.Net.NetworkInformation.Ping).SendPingAsync($Computer, $Timeout, $Buffer, $PingOptions)
            }
        }
        try {
            [void] [Threading.Tasks.Task]::WaitAll($Task.Task)
        } catch {
            Write-Error -Message "Error checking [$Computer]"
        }
        $Task | ForEach-Object {
            if ($_.Task.IsFaulted) {
                $Result = $_.Task.Exception.InnerException.InnerException.Message
                $IPAddress = $Null
                $ResponseTime = $Null
            } else {
                $Result = $_.Task.Result.Status
                $IPAddress = $_.task.Result.Address.ToString()
                $ResponseTime = $_.task.Result.RoundtripTime
            }
            $Layout = [ordered] @{
                    ComputerName = $_.ComputerName
                    IPAddress    = if ($IPAddress) { [version] $IPAddress } else { $Null }
                    Result       = $Result
                    BufferSize   = $BufferSize
                    ResponseTime = $ResponseTime
                    DontFragment = $DontFragment
                    Timeout      = $Timeout
                    TimeToLive   = $TimeToLive
            }
            if ($IncludeSource) {
                $Layout.Insert(0,'Source',$Source)
            }
            $Object = New-Object -TypeName psobject -Property $Layout
            $Object.pstypenames.insert(0, 'Net.AsyncPingResult')
            if ($Full) {
                $Object
            } else {
                if ($IncludeSource) {
                    $Object | Select-Object -Property Source, ComputerName, IPAddress, Result
                } else {
                    $Object | Select-Object -Property ComputerName, IPAddress, Result
                }
            }
        }
        Write-Verbose -Message "Ending [$($MyInvocation.Mycommand)]"
    }
}