|
#requires -version 3 |
|
|
|
$ErrorActionPreference = 'Stop' |
|
|
|
$networkTraceFileName = 'NetworkTrace' |
|
|
|
function LaunchSingleRemoteCommand([string][parameter(mandatory)]$ComputerName, [pscredential]$Credential, [System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication, [scriptblock][parameter(mandatory)]$ScriptBlock) |
|
{ |
|
$args = @{ |
|
ComputerName = $ComputerName |
|
ScriptBlock = $ScriptBlock |
|
} |
|
if($Credential) { |
|
$args.Credential = $Credential |
|
} |
|
if($Authentication) { |
|
$args.Authentication = $Authentication |
|
} |
|
|
|
Invoke-Command @args |
|
} |
|
|
|
function LaunchRemoteCommand([string[]][parameter(mandatory)]$ComputerNames, [pscredential]$Credential, [System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication, [scriptblock][parameter(mandatory)]$ScriptBlock) |
|
{ |
|
$ComputerNames | % { |
|
LaunchSingleRemoteCommand $_ $Credential $Authentication $ScriptBlock |
|
} |
|
} |
|
|
|
function Start-NetworkCapture([string[]][parameter(mandatory)]$ComputerNames, [pscredential]$Credential, [System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication = [System.Management.Automation.Runspaces.AuthenticationMechanism]::Default, [ipaddress]$Ipv4AddressFilter) |
|
{ |
|
LaunchRemoteCommand $ComputerNames $Credential $Authentication { |
|
$tempLocation = Join-Path $env:TEMP $using:networkTraceFileName |
|
if(Test-Path "$tempLocation.*") { |
|
Write-Host "Removing previous capture file..." |
|
rm "$tempLocation.*" |
|
} |
|
$args = 'trace', 'start', 'capture=yes', "traceFile=$tempLocation.etl" |
|
if($using:Ipv4AddressFilter) { |
|
$args += "ipv4.address=$using:Ipv4AddressFilter" |
|
$args += "ethernet.type=ipv4" |
|
} |
|
netsh.exe $args |
|
if(!$?) { |
|
throw "Failed to start capture." |
|
} |
|
} |
|
} |
|
|
|
function Stop-NetworkCapture([string[]][parameter(mandatory)]$ComputerNames, [pscredential]$Credential, [System.Management.Automation.Runspaces.AuthenticationMechanism]$Authentication = [System.Management.Automation.Runspaces.AuthenticationMechanism]::Default) |
|
{ |
|
LaunchRemoteCommand $ComputerNames $Credential $Authentication { |
|
netsh.exe trace stop |
|
if(!$?) { |
|
throw "Failed to stop capture." |
|
} |
|
} |
|
|
|
Write-Host "Copying trace files to the local machine..." |
|
$ComputerNames | % { |
|
$networkTraceFileBase = LaunchSingleRemoteCommand -ComputerName $_ -Credential $Credential -Authentication $Authentication -ScriptBlock { Join-Path $env:TEMP $using:networkTraceFileName} |
|
$remotePathBase = $networkTraceFileBase -replace '^(?<drive>[a-zA-Z]):', "\\$_\`${drive}$" |
|
if($remotePathBase -eq $networkTraceFileBase) { |
|
throw "Failed to translate local path into remote UNC path: $networkTraceFileBase." |
|
} |
|
$remoteFolder = Split-Path $remotePathBase |
|
$remoteFile = (Split-Path -Leaf $remotePathBase) + '.*' |
|
$drive = New-PSDrive -Name NetworkCaptureDrive -PSProvider FileSystem -Root $remoteFolder -Credential $Credential |
|
try { |
|
$tempDirectory = "Working $([guid]::NewGuid())" |
|
md $tempDirectory | Out-Null |
|
cp "NetworkCaptureDrive:\$remoteFile" $tempDirectory |
|
|
|
# Work around file locking bug in Get-WinEvent: |
|
# https://github.com/PowerShell/PowerShell/issues/3979 |
|
$traceFilePath = Resolve-Path "$tempDirectory\NetworkTrace.etl" | select -expand Path |
|
$job = Start-Job -command { $logs = Get-WinEvent -Oldest -Path $using:traceFilePath | select -first 1 -last 1; function FormatEventTime($Event) { ($Event | select -Expand TimeCreated).ToString('o') -replace ':', '-' }; "$(FormatEventTime ($logs | select -First 1)) - $(FormatEventTime ($logs | select -Last 1))" } |
|
$dateRange = $job | Wait-Job | Receive-Job |
|
$job | Remove-Job |
|
|
|
$directoryName = "$dateRange on $_" |
|
|
|
# Work around file locking bug in Get-WinEvent: |
|
# https://github.com/PowerShell/PowerShell/issues/3979 |
|
$timer = [System.Diagnostics.Stopwatch]::StartNew() |
|
$success = $false; |
|
while($timer.ElapsedMilliseconds -lt 20000) { |
|
try { |
|
mv $tempDirectory $directoryName |
|
$success = $true |
|
} |
|
catch { |
|
# we'll try again |
|
} |
|
} |
|
if(!$success) { |
|
throw "Failed to rename directory $tempDirectory to $directoryName." |
|
} |
|
} finally { |
|
Remove-PSDrive $drive |
|
} |
|
} |
|
} |
|
|
|
function Convert-CaptureFilesToWiresharkFormat |
|
{ |
|
ls -r *.etl | % { |
|
$sourceFile = $_.FullName |
|
$destinationFile = [io.path]::ChangeExtension($sourceFile, 'cap') |
|
if(!(Test-Path $destinationFile)) { |
|
$session = New-PefTraceSession -Path $destinationFile -SaveOnStop |
|
$session | Add-PefMessageProvider -Provider $sourceFile |
|
$session | Start-PefTraceSession |
|
} |
|
} |
|
} |
|
|
|
Export-ModuleMember Start-NetworkCapture |
|
Export-ModuleMember Stop-NetworkCapture |
|
Export-ModuleMember Convert-CaptureFilesToWiresharkFormat |
To override the default file size append $args with
'maxSize=0', 'fileMode=single'
Reference: How to run a NETSH Trace