-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGet-NetworkMetric.ps1
More file actions
445 lines (370 loc) · 17.6 KB
/
Copy pathGet-NetworkMetric.ps1
File metadata and controls
445 lines (370 loc) · 17.6 KB
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
function Get-NetworkMetric
{
<#
.SYNOPSIS
Collects comprehensive network performance metrics for a target host
.DESCRIPTION
Performs multiple network tests against a target host and returns detailed metrics
including latency, packet loss, jitter, and DNS resolution time. Uses cross-platform
.NET methods for compatibility across Windows, macOS, and Linux.
Metrics collected:
- Latency (min/max/avg)
- Packet loss percentage
- Jitter (latency variance)
- DNS resolution time
- Success/failure counts
UNDERSTANDING METRICS:
This function provides the raw data needed for network diagnostics. Understanding
these metrics helps identify specific network issues:
1. LATENCY METRICS (LatencyMin/Max/Avg)
Round-trip time for packets to reach destination and return:
- Good: < 50ms (suitable for most applications)
- Acceptable: 50-100ms (noticeable for interactive apps)
- Poor: 100-200ms (degraded user experience)
- Critical: > 200ms (unusable for real-time applications)
Min vs Max difference analysis:
- Small difference (< 20ms): Stable, predictable connection
- Medium difference (20-50ms): Some variance, generally acceptable
- Large difference (> 50ms): High variability, investigate cause
2. PACKET LOSS ANALYSIS
Percentage of packets that fail to reach destination:
- 0%: Perfect connectivity (expected for most services)
- 1-2%: Minor issues, acceptable for web browsing
- 2-5%: Noticeable degradation for VoIP/video
- 5-10%: Significant issues, poor user experience
- > 10%: Critical problems requiring immediate attention
Common causes to investigate:
- Network congestion (QoS configuration needed)
- Firewall dropping packets (rule misconfiguration)
- Physical layer issues (bad cable, WiFi interference)
- Route flapping or unstable BGP routing
3. JITTER ANALYSIS
Standard deviation of latency measuring connection consistency:
- Excellent: < 10ms (suitable for VoIP, gaming, real-time apps)
- Good: 10-30ms (acceptable for most applications)
- Poor: 30-50ms (degraded quality for real-time apps)
- Critical: > 50ms (unusable for VoIP/gaming)
High jitter indicates:
- Network congestion with variable queuing delays
- Route changes occurring during measurement period
- Shared bandwidth with bursty traffic patterns
- WiFi interference or poor signal quality
4. DNS RESOLUTION TIME
Time taken to resolve hostname to IP address:
- Fast: < 50ms (well-configured DNS infrastructure)
- Acceptable: 50-100ms (typical for remote DNS servers)
- Slow: 100-200ms (DNS server overloaded or geographically distant)
- Critical: > 200ms (DNS performance issue requiring attention)
High DNS time suggests:
- DNS server overloaded or performing slowly
- Network path to DNS server is congested
- DNS server should be changed to closer/faster alternative
- DNS cache not being utilized effectively
See EXAMPLES for practical troubleshooting workflows and metric analysis scenarios.
RELATED FUNCTIONS:
This function is designed to be used by:
- Invoke-NetworkDiagnostic: Calls Get-NetworkMetric to collect data for each host
- Show-NetworkLatencyGraph: Uses Get-NetworkMetric in continuous mode to gather latency samples
Can also be used standalone for custom network metric collection and analysis. .PARAMETER HostName
Target hostname or IP address to test
.PARAMETER Count
Number of test iterations to perform (default: 10)
.PARAMETER Timeout
Timeout in milliseconds for each test (default: 2000)
.PARAMETER Port
TCP port to test (default: 443)
.PARAMETER IncludeDns
Include DNS resolution time in metrics
.PARAMETER SampleDelayMilliseconds
Delay between samples in milliseconds (default: 100). Set to 0 for back-to-back samples.
.EXAMPLE
PS > Get-NetworkMetric -HostName 'bing.com' -Count 20
Collects 20 samples of network metrics for bing.com
.EXAMPLE
PS > Get-NetworkMetric -HostName '1.1.1.1' -Port 53 -IncludeDns
Tests DNS server with DNS resolution metrics
.EXAMPLE
PS > Get-NetworkMetric -HostName 'github.com' -Count 50 | Format-List
Collects 50 samples and displays detailed metrics in list format
.EXAMPLE
PS > $metrics = Get-NetworkMetric -HostName 'api.example.com' -Port 8080 -Count 100
PS > $metrics.LatencyData | Measure-Object -Average -Minimum -Maximum
Collect metrics and perform custom analysis on latency data
.EXAMPLE
PS > Get-NetworkMetric -HostName 'database.local' -Port 5432 -Timeout 5000 -Count 30
Test database server with 5-second timeout and 30 samples
.EXAMPLE
PS > 'bing.com', 'cloudflare.com', 'github.com' | Get-NetworkMetric -Count 25
Test multiple hosts via pipeline with 25 samples each
.EXAMPLE
PS > $result = Get-NetworkMetric -HostName 'vpn.company.com' -IncludeDns
PS > if ($result.PacketLoss -gt 5) { Write-Warning "High packet loss: $($result.PacketLoss)%" }
Collect metrics and conditionally alert on high packet loss
.EXAMPLE
PS > Get-NetworkMetric -HostName '8.8.8.8' -Port 53 -Count 20 | Select-Object HostName, PacketLoss, LatencyAvg, Jitter
Collect DNS server metrics and display specific properties
.EXAMPLE
PS > $metrics = Get-NetworkMetric -HostName 'server.local' -Port 22 -Count 40
PS > $metrics.LatencyData | Export-Csv -Path latency-log.csv -NoTypeInformation
Collect metrics and export raw latency data to CSV for further analysis
.EXAMPLE
PS > # TROUBLESHOOTING: Slow application performance
PS > $metrics = Get-NetworkMetric -HostName 'app-server.com' -Count 30
PS > if ($metrics.LatencyAvg -gt 100) {
>> "High latency detected: $($metrics.LatencyAvg)ms (threshold: 100ms)"
>> "Check: Network path, routing, server location"
>> }
PS > if ($metrics.Jitter -gt 30) {
>> "High jitter detected: $($metrics.Jitter)ms (threshold: 30ms)"
>> "Check: Network congestion, QoS configuration"
>> }
Diagnose application performance. Metrics point to specific network layer issues.
Latency >100ms or Jitter >30ms indicate network problems.
.EXAMPLE
PS > # TROUBLESHOOTING: VoIP quality issues
PS > # VoIP requirements: latency < 150ms, jitter < 30ms, packet loss < 1%
PS > $voip = Get-NetworkMetric -HostName 'sip.company.com' -Port 5060 -Count 100
PS > $issues = @()
PS > if ($voip.LatencyAvg -gt 150) { $issues += "Latency: $($voip.LatencyAvg)ms" }
PS > if ($voip.Jitter -gt 30) { $issues += "Jitter: $($voip.Jitter)ms" }
PS > if ($voip.PacketLoss -gt 1) { $issues += "Packet Loss: $($voip.PacketLoss)%" }
PS > if ($issues) { "VoIP quality issues: $($issues -join ', ')" }
Evaluate VoIP connection quality. Identify specific metrics failing VoIP requirements.
Any metric exceeding thresholds will cause call quality degradation.
.EXAMPLE
PS > # TROUBLESHOOTING: Intermittent connection problems
PS > $extended = Get-NetworkMetric -HostName 'unreliable-host.com' -Count 200 -SampleDelayMilliseconds 50
PS > $failures = $extended.LatencyData | Where-Object { $null -eq $_ }
PS > "Total failures: $($failures.Count) out of $($extended.SamplesTotal)"
PS > "Failure rate: $($extended.PacketLoss)%"
PS > # Check if failures are clustered or random
PS > $extended.LatencyData | ForEach-Object -Begin { $i=0 } -Process {
>> if ($null -eq $_) { "Failure at sample $i" }
>> $i++
>> }
Diagnose intermittent issues. Pattern of failures (clustered vs random) indicates
cause: clustered = routing flaps, random = congestion/interference.
.EXAMPLE
PS > # WORKFLOW: Comparing service endpoints
PS > $endpoints = @('api-us-east.com', 'api-us-west.com', 'api-eu.com')
PS > $results = foreach ($ep in $endpoints) {
>> Get-NetworkMetric -HostName $ep -Port 8080 -Count 50
>> }
PS > $results | Select-Object HostName,LatencyAvg,Jitter,PacketLoss | Format-Table
PS > # Rank by overall quality
PS > $best = $results | Sort-Object -Property @(
>> @{Expression={$_.PacketLoss}; Ascending=$true},
>> @{Expression={$_.LatencyAvg}; Ascending=$true},
>> @{Expression={$_.Jitter}; Ascending=$true}
>> ) | Select-Object -First 1
PS > "Best endpoint: $($best.HostName)"
Compare multiple endpoints quantitatively. Rank by packet loss (priority), then
latency, then jitter to identify optimal endpoint.
.EXAMPLE
PS > # WORKFLOW: DNS performance investigation
PS > $dns1 = Get-NetworkMetric -HostName '8.8.8.8' -Port 53 -IncludeDns -Count 30
PS > $dns2 = Get-NetworkMetric -HostName '1.1.1.1' -Port 53 -IncludeDns -Count 30
PS > "Google DNS: $($dns1.DnsResolution)ms avg latency: $($dns1.LatencyAvg)ms"
PS > "Cloudflare DNS: $($dns2.DnsResolution)ms avg latency: $($dns2.LatencyAvg)ms"
PS > $site = Get-NetworkMetric -HostName 'slow-website.com' -IncludeDns -Count 20
PS > if ($site.DnsResolution -gt $site.LatencyAvg) {
>> "DNS resolution ($($site.DnsResolution)ms) is slower than network latency ($($site.LatencyAvg)ms)"
>> "Consider: Using different DNS server or checking DNS infrastructure"
>> }
Identify whether DNS or network latency is the bottleneck. Compare DNS resolution
time against network latency to isolate the problem.
.EXAMPLE
PS > # WORKFLOW: Service health baseline and comparison
PS > # Step 1: Create baseline during healthy period
PS > $baseline = Get-NetworkMetric -HostName 'prod-database.com' -Port 3306 -Count 100
PS > $baseline | Export-Clixml -Path 'db-baseline.xml'
PS > # Step 2: During incident, compare to baseline
PS > $current = Get-NetworkMetric -HostName 'prod-database.com' -Port 3306 -Count 100
PS > $baseline = Import-Clixml -Path 'db-baseline.xml'
PS > $degradation = [PSCustomObject]@{
>> LatencyIncrease = $current.LatencyAvg - $baseline.LatencyAvg
>> JitterIncrease = $current.Jitter - $baseline.Jitter
>> PacketLossChange = $current.PacketLoss - $baseline.PacketLoss
>> }
PS > $degradation | Format-List
Quantify performance degradation vs. known good baseline. Establish baseline during
healthy periods, then compare during incidents to measure impact.
.OUTPUTS
PSCustomObject with network metrics
.NOTES
Author: Jon LaBelle
License: MIT
.LINK
https://github.com/jonlabelle/pwsh-profile/blob/main/Functions/NetworkAndDns/Get-NetworkMetric.ps1
Source: https://github.com/jonlabelle/pwsh-profile/blob/main/Functions/NetworkAndDns/Get-NetworkMetric.ps1
#>
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
[Parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[String]$HostName,
[Parameter()]
[ValidateRange(1, 1000)]
[Int32]$Count = 10,
[Parameter()]
[ValidateRange(100, 30000)]
[Int32]$Timeout = 2000,
[Parameter()]
[ValidateRange(1, 65535)]
[Int32]$Port = 443,
[Parameter()]
[Switch]$IncludeDns,
[Parameter()]
[ValidateRange(0, 5000)]
[Int32]$SampleDelayMilliseconds = 100
)
begin
{
Write-Verbose "Starting network metrics collection for $HostName"
# Initialize collections (allow nulls so we can mark failed samples)
$latencies = [System.Collections.Generic.List[Nullable[Double]]]::new()
$successCount = 0
$failureCount = 0
$dnsTime = $null
# DNS resolution if requested
if ($IncludeDns)
{
Write-Verbose 'Measuring DNS resolution time'
$dnsStopwatch = [System.Diagnostics.Stopwatch]::StartNew()
try
{
$null = [System.Net.Dns]::GetHostAddresses($HostName)
$dnsStopwatch.Stop()
$dnsTime = $dnsStopwatch.Elapsed.TotalMilliseconds
Write-Verbose "DNS resolution: $([Math]::Round($dnsTime, 2))ms"
}
catch
{
Write-Verbose "DNS resolution failed: $($_.Exception.Message)"
$dnsTime = $null
}
}
}
process
{
Write-Verbose "Testing connectivity to ${HostName}:${Port} ($Count samples)"
# Reuse stopwatch across iterations to reduce allocations
$stopwatch = [System.Diagnostics.Stopwatch]::new()
for ($i = 1; $i -le $Count; $i++)
{
$stopwatch.Restart()
$tcpClient = $null
try
{
$tcpClient = [System.Net.Sockets.TcpClient]::new()
# Use BeginConnect/EndConnect with timeout for broad compatibility
$asyncResult = $tcpClient.BeginConnect($HostName, $Port, $null, $null)
$waitHandle = $asyncResult.AsyncWaitHandle
if ($waitHandle.WaitOne($Timeout, $false))
{
try
{
$tcpClient.EndConnect($asyncResult)
$stopwatch.Stop()
$latency = $stopwatch.Elapsed.TotalMilliseconds
$latencies.Add($latency)
$successCount++
Write-Verbose "Sample $i/$Count : $([Math]::Round($latency, 2))ms"
}
catch
{
$stopwatch.Stop()
Write-Verbose "Sample $i/$Count : Connection failed - $($_.Exception.Message)"
$latencies.Add($null)
$failureCount++
}
}
else
{
# Timeout occurred - dispose immediately to avoid lingering handles
$stopwatch.Stop()
Write-Verbose "Sample $i/$Count : Timeout"
$latencies.Add($null)
$failureCount++
$tcpClient.Close()
$tcpClient.Dispose()
$tcpClient = $null
}
}
catch
{
$stopwatch.Stop()
Write-Verbose "Sample $i/$Count : Failed - $($_.Exception.Message)"
$latencies.Add($null)
$failureCount++
}
finally
{
if ($null -ne $tcpClient)
{
$tcpClient.Close()
$tcpClient.Dispose()
}
}
# Small delay between requests
if ($i -lt $Count -and $SampleDelayMilliseconds -gt 0)
{
Start-Sleep -Milliseconds $SampleDelayMilliseconds
}
}
}
end
{
# Calculate metrics from valid latencies in a single pass
$packetLoss = if ($Count -gt 0) { [Math]::Round(($failureCount / $Count) * 100, 2) } else { 0 }
$validCount = 0
$minLatency = [Double]::PositiveInfinity
$maxLatency = [Double]::NegativeInfinity
$sumLatency = 0.0
$sumLatencySq = 0.0
foreach ($latency in $latencies)
{
if ($null -eq $latency)
{
continue
}
$value = [double]$latency
$validCount++
if ($value -lt $minLatency) { $minLatency = $value }
if ($value -gt $maxLatency) { $maxLatency = $value }
$sumLatency += $value
$sumLatencySq += ($value * $value)
}
if ($validCount -gt 0)
{
$avgLatency = $sumLatency / $validCount
$variance = ([Math]::Max(0, ($sumLatencySq / $validCount) - ($avgLatency * $avgLatency)))
$jitter = [Math]::Sqrt($variance)
}
else
{
$minLatency = $null
$maxLatency = $null
$avgLatency = $null
$jitter = $null
}
Write-Verbose 'Metrics collection completed'
# Return metrics object
[PSCustomObject]@{
HostName = $HostName
Port = $Port
SamplesTotal = $Count
SamplesSuccess = $successCount
SamplesFailure = $failureCount
PacketLoss = $packetLoss
LatencyMin = if ($null -ne $minLatency) { [Math]::Round($minLatency, 2) } else { $null }
LatencyMax = if ($null -ne $maxLatency) { [Math]::Round($maxLatency, 2) } else { $null }
LatencyAvg = if ($null -ne $avgLatency) { [Math]::Round($avgLatency, 2) } else { $null }
Jitter = if ($null -ne $jitter) { [Math]::Round($jitter, 2) } else { $null }
DnsResolution = if ($null -ne $dnsTime) { [Math]::Round($dnsTime, 2) } else { $null }
LatencyData = $latencies.ToArray()
Timestamp = Get-Date
}
}
}