Tshark captures on remote server with foreach -parallel loop on powershell - powershell

I'm traing to start 2 remote packet captures at same time on a winServer2016 with two nics, with tshark 3.4.5 and powershell 7.2.1.
The problem is that I can't found the correct sintax to pass variables to tshark...
$ScriptPath = "E:\CC12\Scripts\TShark"
$dateForDirLogs = (Get-Date).ToString('yyyy-MM-dd-HH')
$dateForLogs = (Get-Date).ToString('yyyy-MM-dd-HH.mm')
$logDir = "$ScriptPath\Logs"
$MyServer = "SRV1"
$LogFile = "$logDir\$($MyServer)_$($dateForLogs).txt"
$MySession = New-PSSession $MyServer -ConfigurationName PowerShell.7.2.1
Invoke-Command -Session $MySession -ScriptBlock {
$CptPathName = "E:\Captures\$using:dateForDirLogs"
$JobLogFile = "$CptPathName\RmJobsLog.txt"
$HName = $using:MyServer
$CptFilePub = "$CptPathName\$($HName)_PUB_$using:dateForLogs.pcapng"
$CptFilePri = "$CptPathName\$($HName)_PRI_$using:dateForLogs.pcapng"
$TsharkBin = "C:\Program Files\Wireshark\tshark.exe"
[string]$NicPub = & $TsharkBin -D | Select-String "Public"
$ifNumPub = $NicPub.Split("\")[0]
$NicPub = $NicPub.Replace($ifNumPub,'')
$NicPub = $NicPub.Replace(' (Public)','')
[string]$NicPri = & $TsharkBin -D | Select-String "Private"
$ifNumPri = $NicPri.Split("\")[0]
$NicPri = $NicPri.Replace($ifNumPri,'')
$NicPri = $NicPri.Replace(' (Private)','')
$FilterPub = "dst net 10.49.94.0/24"
$FilterTimePub = 300
$FilterSizePub = 307200
$FilterPri = "dst net 10.56.128.0/25"
$FilterTimePri = 300
$FilterSizePri = 307200
# echo variables
$CptPathName
$JobLogFile
$HName
$CptFilePub
$CptFilePri
$NicPub
$NicPri
$FilterPub
$FilterPri
$CptCMDs = #(
"-i $NicPub -f $FilterPub -a duration:$FilterTimePub -w $CptFilePub"
"-i $NicPri -f $FilterPri -a duration:$FilterTimePri -w $CptFilePri"
)
$CptCMDs | ForEach-Object -Parallel {
$TsharkBin = "C:\Program Files\Wireshark\tshark.exe"
& $TsharkBin $_
} -ThrottleLimit 2
}
I've tried to quoting the "qoutes" like that, but doesn't works:
$NicPub = "`"$NicPub`""
Seems that tshark reads the $CptCMDs as a single parameter the interface name! Seems skips its commands switch...
This is the output, if I ran the script interactively:
E:\Captures\2022-09-16-15
E:\Captures\2022-09-16-15\RmJobsLog.txt
SRV1
E:\Captures\2022-09-16-15\SRV1_PUB_2022-09-16-15.06.pcapng
E:\Captures\2022-09-16-15\SRV1_PRI_2022-09-16-15.06.pcapng
"\Device\NPF_{3578AB86-0318-4116-818C-87BC171F2B6F}"
"\Device\NPF_Loopback"
dst net 10.49.94.0/24
dst net 10.56.128.0/25
Capturing on ' \Device\NPF_{3578AB86-0318-4116-818C-87BC171F2B6F} -f dst net 10.49.94.0/24 -a duration:300 -w E:\Captures\2022-09-16-15\SRV1_PUB_2022-09-16-15.06.pcapng'
Capturing on ' \Device\NPF_{34234H86-5488-5546-212C-57867G57FR2Y} -f dst net 10.56.128.0/25 -a duration:300 -w E:\Captures\2022-09-16-15\SRV1_PRI_2022-09-16-15.06.pcapng'
tshark: The capture session could not be initiated on interface ' \Device\NPF_{3578AB86-0318-4116-818C-87BC171F2B6F} -f dst net 10.49.94.0/24) -a duration:300 -w E:\Captures\2022-09-16-15\SRV1_PUB_2022-09-16-15.06.pcapng' (Error opening adapter: The filename, directory name, or volume label syntax is incorrect. (123)).
Please check that you have the proper interface or pipe specified.
0 packets captured
tshark: The capture session could not be initiated on interface ' \Device\NPF_{34234H86-5488-5546-212C-57867G57FR2Y} -f dst net 10.56.128.0/25 -a duration:300 -w E:\Captures\2022-09-16-15\SRV1_PRI_2022-09-16-15.06.pcapng' (Error opening adapter: The filename, directory name, or volume label syntax is incorrect. (123)).
Please check that you have the proper interface or pipe specified.
0 packets capture
Can Anyone help me?

Somehow the arguments would have to be split into a list like:
$list1 = 1,2,3
$list2 = 4,5,6
$prog = 'echoargs'
$list1,$list2 | % { & $prog $_ }
Arg 0 is <1>
Arg 1 is <2>
Arg 2 is <3>
Arg 0 is <4>
Arg 1 is <5>
Arg 2 is <6>
Try it this way:
$list1 = '-i',$NicPub,'-f',$FilterPub,'-a',"duration:$FilterTimePub",'-w',$CptFilePub
$list2 = '-i',$NicPri,'-f',$FilterPri,'-a',"duration:$FilterTimePri",'-w',$CptFilePri
$CptCMDs = $list1,$list2
$CptCMDs | ForEach-Object -Parallel {
# ...

Related

How to Ignore lines with StreamWriter WriteLine

trying to figure out how to ignore or stop specific lines from being written to file with StreamWriter. Here is the code I'm working with from How to pass arguments to program when using variable as path :
$LogDir = "c:\users\user" # Log file output directory
$PlinkDir = "C:" # plink.exe directory
$SerialIP = "1.1.1.1" # serial device IP address
$SerialPort = 10000 # port to log
function CaptureWeight {
Start-Job -Name WeightLog -ScriptBlock {
filter timestamp {
$sw.WriteLine("$(Get-Date -Format MM/dd/yyyy_HH:mm:ss) $_")
}
try {
$sw = [System.IO.StreamWriter]::new("$using:LogDir\WeightLog_$(Get-Date -f MM-dd-yyyy).txt")
& "$using:PlinkDir\plink.exe" -telnet $using:SerialIP -P $using:SerialPort | TimeStamp
}
finally {
$sw.ForEach('Flush')
$sw.ForEach('Dispose')
}
}
}
$job = CaptureWeight # For testing, save the job
Start-Sleep -Seconds 60 # wait 1 minute
$job | Stop-Job # kill the job
Get-Content "$LogDir\WeightLog_$(Get-Date -f MM-dd-yyyy).txt" # Did it work?
And the output is this:
05/09/2022_14:34:19 G+027800 lb
05/09/2022_14:34:20
05/09/2022_14:34:20 G+027820 lb
05/09/2022_14:34:21
05/09/2022_14:34:21 G+027820 lb
05/09/2022_14:34:22
05/09/2022_14:34:22 G+027820 lb
Without the TimeStamp, every other line is blank. I have a couple lines to cleanup the logs, one removes every other line one removes lines with zero weights:
Set-Content -Path "$LogDir\WeightLog_$(get-date -f MM-dd-yyyy).txt" -Value (get-content -Path "$LogDir\WeightLog_$(get-date -f MM-dd-yyyy).txt" | Where-Object { $i % 2 -eq 0; $i++ })
Set-Content -Path "$LogDir\WeightLog_$(get-date -f MM-dd-yyyy).txt" -Value (get-content -Path "$LogDir\WeightLog_$(get-date -f MM-dd-yyyy).txt" | Select-String -Pattern '00000' -NotMatch)
If files get to be too large these can take a while to run, would be nice to not have them written to start with.
Thanks!
Edit, This is what I ended up with:
#****************Serial Scale Weight Logger********************
$LogDir = "c:\ScaleWeightLogger\Logs" # Log File Output Directory
$PlinkDir = "c:\ScaleWeightLogger" # plink.exe Directory
$SerialIP = "1.1.1.1" # Serial Device IP Address
$SerialPort = "10000" # Serial Device Port to Log
$MakeWeight = "000\d\d\d" # Minimum weight to log
[datetime]$JobEndTime = '23:58' # "WeightLog" Job End Time
[datetime]$JobStartTime = '00:02' #Use '8/24/2024 03:00' for a date in the future
# https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_regular_expressions
function StartWeightCapture {
Start-Job -Name WeightLog -ScriptBlock {
filter timestamp {
# Set Output Filter, Do Not Write Blank Lines or Weight Matching...
if([string]::IsNullOrWhiteSpace($_) -or $_ -match $using:MakeWeight) {
# skip it
return
}
# Set TimeStamp Format Filter
$sw.WriteLine("$(Get-Date -Format MM/dd/yyyy_HH:mm:ss) $_")
}
try {
# Set File Path, Set $true to Append
$sw = [System.IO.StreamWriter]::new("$using:LogDir\WeightLog_$(Get-Date -f MM-dd-yyyy).txt", $true)
# Keep Memory Buffer Clear After Writting
$sw.AutoFlush = $true
# Start plink, Filter Output, Append TimeStamp
& "$using:PlinkDir\plink.exe" -telnet $using:SerialIP -P $using:SerialPort | TimeStamp
}
finally {
# Discard Data After Writing
$sw.ForEach('Flush')
$sw.ForEach('Dispose')
}
}
}
function WeightCaptureEndTime {
[datetime]$CurrentTime = Get-Date
[int]$WaitSeconds = ( $JobEndTime - $CurrentTime ).TotalSeconds
Start-Sleep -Seconds $WaitSeconds
}
function StopWeightCapture {
Stop-Job WeightLog
$AddDaysWhenInPast = 1
[datetime]$CurrentTime = Get-Date
If ($JobStartTime -lt $CurrentTime) { $JobStartTime = $JobStartTime.AddDays($AddDaysWhenInPast) }
[int]$WaitSeconds = ( $JobStartTime - $CurrentTime ).TotalSeconds
Start-Sleep -Seconds $WaitSeconds
}
while ($true) {
StartWeightCapture
WeightCaptureEndTime
StopWeightCapture
}
I'm launching it at boot with:
powershell -windowstyle hidden -ExecutionPolicy bypass "& "C:\ScaleWeightLogger\ScaleWeightLogger.ps1"" & exit
And got this to end it manually since it's in the background. It only grabs the PID of the main powershell process and not the job:
#echo off
for /F "tokens=2" %%K in ('
tasklist /FI "ImageName eq powershell.exe" /FI "Status eq Running" /FO LIST ^| findstr /B "PID:"
') do (
echo "PID is %%K, Ending process..."
taskkill /F /PID %%K
)
pause
exit
If I understand correctly, adding this condition should avoid you the trouble of having to read the logs and skip the unwanted lines.
See String.IsNullOrWhiteSpace(String) Method and -match matching operator for details.
filter timestamp {
# if this output is purely whitespace or it matches `00000`
if([string]::IsNullOrWhiteSpace($_) -or $_ -match '00000') {
# skip it
return
}
$sw.WriteLine("$(Get-Date -Format MM/dd/yyyy_HH:mm:ss) $_")
}
Regarding the observation noted in previous question:
...when trying view the file while it's running, it seems like it updates (for viewing) about every 2 minutes, you get one 2 minute chunk of data that is about 2 minutes behind, the 2 minutes of data is there...
For this, you can enable the AutoFlush property from your StreamWriter.
Remarks has an excellent explanation of when it's worth enabling this property as well as the performance implications:
When AutoFlush is set to false, StreamWriter will do a limited amount of buffering, both internally and potentially in the encoder from the encoding you passed in. You can get better performance by setting AutoFlush to false, assuming that you always call Close (or at least Flush) when you're done writing with a StreamWriter.
For example, set AutoFlush to true when you are writing to a device where the user expects immediate feedback. Console.Out is one of these cases: The StreamWriter used internally for writing to Console flushes all its internal state except the encoder state after every call to StreamWriter.Write.
$sw = [System.IO.StreamWriter]::new("$using:LogDir\WeightLog_$(Get-Date -f MM-dd-yyyy).txt")
$sw.AutoFlush = $true

Why is there a $ before the name of a variable in coding

What does the $ symbol mean? I saw it in this script:
function sendColor($colorName)
{
$portInfo = ( Get-WmiObject Win32_SerialPort | Where { $_.PNPDeviceID -like '*VID_239A*' } | select -last 1 )
$port = new-Object System.IO.Ports.SerialPort $portInfo.DeviceID,9600,None,8,one
$port.open()
$text = $colorName + "`r"
$port.Write($text)
start-sleep -m 50
$port.ReadExisting()
$port.Close()
}
if ($args.Length -eq 0)
{
echo "Usage: ColorChange <color>"
}
else
{
sendColor($args[0])
}
As far as I know this is a PowerShell script, but I'm not sure.
Taken from the documentation in:
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_variables?view=powershell-7
"A variable is a unit of memory in which values are stored. In PowerShell, variables are represented by text strings that begin with a dollar sign ($), such as $a, $process, or $my_var."

Pass string array through nested Invoke-Expression

My usecase consists 3 Scripts:
Script A.ps1 ← start script
Script B.ps1 ← script in the middle
Script C.ps1 ← target script to execute
Constraints: process spaces in path string[] array variables
Script A.ps1:
#SCRIPT EXPRESSION (TO PASS AS ARGUMENT)
$scriptC = "Script C.ps1"
$major = 1
$minor = 2
[string[]]$output = "Output A.txt", "Output B.txt", "Output C.txt"
$scriptExpression = 'powershell -File \"{0}\" -Major {1} -Minor {2} -Output {3}' -f ($scriptC, $major, $minor, $output)
Write-Host $scriptExpression -ForegroundColor Green
#SCRIPT EXPRESSION (TO CALL)
$scriptB = "Script B.ps1"
$config = "Debug"
$flag = 1
[string[]]$array = "String with space A", "String with space B"
$params = #("-File", "$scriptB", "-Config", $config, "-Flag", "$flag", "-ScriptExpression", $scriptExpression, "-Array", $array)
Write-Host "& powershell $params" -ForegroundColor Green
& powershell $params
Script B.ps1:
Param([string]$Config, [int]$Flag, [string]$ScriptExpression, [string[]]$Array)
Write-Host "`n_______________________________________________________________________________________`n"
Write-Host "Script B:`nConfig = $Config ($($Config.GetType()))`nFlag = $Flag ($($Flag.GetType()))`nScriptExpression = $ScriptExpression ($($ScriptExpression.GetType()))`nArray = $Array (Type: $($Array.GetType()) Length: $($Array.Length) <- WRONG LENGTH (2))" -ForegroundColor Yellow
Write-Host "`nCall Script C!" -ForegroundColor Yellow
Invoke-Expression $ScriptExpression
Script C.ps1:
Param([int]$Major, [int]$Minor, [string[]]$Output = #())
Write-Host "`n_______________________________________________________________________________________`n"
Write-Host "Script C:`nMajor = $Major ($($Major.GetType()))`nMinor = $Minor ($($Minor.GetType()))`nOutput = $Output (Type: $($Output.GetType()), Length: $($Output.Length)) <- WRONG EVERYTHING" -ForegroundColor Green
Output:
powershell -File \"Script C.ps1\" -Major 1 -Minor 2 -Output System.String[]
& powershell -File Script B.ps1 -Config Debug -Flag 1 -ScriptExpression powershell -File \"Script C.ps1\" -Major 1 -Minor 2 -Output System.String[] -Array System.String[]
_______________________________________________________________________________________
Script B:
Config = Debug (string)
Flag = 1 (int)
ScriptExpression = powershell -File "Script C.ps1" -Major 1 -Minor 2 -Output System.String[] (string)
Array = String with space A String with space B (Type: string[] Length: 1 ← WRONG LENGTH (2))
Call Script C!
_______________________________________________________________________________________
Script C:
Major = 1 (int)
Minor = 2 (int)
Output = System.String[] (Type: string[], Length: 1) ← WRONG EVERYTHING
How is it possible to pass these string[] arrays with spaces to another script?
I could achieve to call a Script C.ps1 from Script A.ps1 over Script B.ps1 with: (Content of Script B and C is in question)
$output = #("Output A.txt", "Output B.txt", "Output C.txt")
$array = #("String with space A", "String with space B")
#INVOKE EXPRESSION SCRIPT C
$varScript = $("'{0}\Script C.ps1'" -f ($PSScriptRoot))
$varMajor = 1
$varMinor = 2
$varOutput = "'" + $($output -join "','") + "'"
$expression = "& $varScript -Major $varMajor -Minor $varMinor -Output $varOutput"
$expression = """{0}""" -f ($expression) #WRAP IN DOUBLE QUOTES
#INVOKE EXPRESSION SCRIPT B
$varScript = $("'{0}\Script B.ps1'" -f ($PSScriptRoot))
$varConfig = "Debug"
$varFlag = 1
$varArray = "'" + $($array -join "','") + "'"
$call = "& $varScript -Config $varConfig -Flag $varFlag -ScriptExpression $expression -Array $varArray"
Invoke-Expression $call
Output:
Script B:
Config = Debug (string)
Flag = 1 (int)
ScriptExpression = & 'C:\ScriptTest\Script C.ps1' -Major 1 -Minor 2 -Output 'Output A.txt','Output B.txt','Output
C.txt' (string)
Array = String with space A String with space B (Type: string[] Length: 2)
Call Script C!
_______________________________________________________________________________________
Script C:
Major = 1 (int)
Minor = 2 (int)
Output = Output A.txt Output B.txt Output C.txt (Type: string[], Length: 3)
- Output A.txt
- Output B.txt
- Output C.txt

How to use powershell to get a string of output results ?

When I use this command :
ipptool -tv -I -d "doc-uri=http://www.nice.com/123.txt"
The output results is:
media-col (collection) = {media-source=auto}
job-uri (uri) = ipp://192.168.0.1/ipp/print/job-0030
job-id (integer) = 30
job-state (enum) = processing
job-state-reasons (keyword) = none
job-state-message (textWithoutLanguage) =
EXPECTED: STATUS successful-ok (got successful-ok-ignored-or-substituted-attributes)
How Can I use powershell to get the string "30" ?
30 is the value of job-id
Assuming the output result of your ipptool is in a text file called "C:\Test\Iptool.txt" you could use this:
$iptoolOutput = Get-Content "C:\Test\Iptool.txt" -Raw
$result = ''
if ($iptoolOutput -match 'job-id.+=\s*(\d+)') {
$result = $matches[1]
}
$result
Using Select-String:
$ipptool = $(ipptool -tv -I -d "doc-uri=http://www.nice.com/123.txt")
[Int]($ipptool | Select-String "job-id \(integer\) =\s*(\d+)").Matches.Groups[1].Value
You can use Match and Split
$Data = ipptool -tv -I -d "doc-uri=http://www.nice.com/123.txt"
($Data | ? {$_ -match 'job-id'}).Split("=")[1]

Equivalent of `type -a` in Powershell

I am attempting to build my PowerShell troubleshooting skills, and I was hoping to find a similar tool to type in bash.
Example usage of type:
17:07 Mac Shell: Git/>$ type -a shell_session_save
shell_session_save is a function
shell_session_save ()
{
if [ -n "$SHELL_SESSION_FILE" ]; then
echo -n 'Saving session...';
( umask 077;
echo 'echo Restored session: "$(date -r '$(date +%s)')"' >|"$SHELL_SESSION_FILE" );
declare -F shell_session_save_user_state > /dev/null && shell_session_save_user_state;
shell_session_history_allowed && shell_session_save_history;
echo 'completed.';
fi
}
17:07 Mac Shell: Git/>$
I just want an easy way to view function code in PowerShell's CLI. Is this possible natively?
Thanks for any help.
It is available natively using Get-Command as such:
Get-Command MyFunctionName -ShowCommandInfo
Here's the output of one of my functions that I keep on hand named Get-OutputFilePath:
Name : Get-OutputFilePath
ModuleName :
Module : #{Name=}
CommandType : Function
Definition :
[CmdletBinding()]
Param(
[String]$Filter = "All Files (*.*)|*.*",
[String]$InitialDirectory,
[Parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)]
[Alias('DefaultFileName')]
[String]$FullName,
[Switch]$Force)
BEGIN{
[void][System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms")
}
PROCESS{
If($FullName -match "\\.+$" -and !$InitialDirectory){$InitialDirectory = Split-Path $FullName;$FullName = Split-Path $FullName -Leaf}ElseIf(!$InitialDirectory){$InitialDirectory=[Environment]::GetFolderPath('Desktop')}
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.initialDirectory = $InitialDirectory
Try{$SaveFileDialog.filter = $Filter}Catch{Throw $_;Break}
$SaveFileDialog.FileName = $FullName
$SaveFileDialog.OverwritePrompt = !$Force
If($SaveFileDialog.ShowDialog() -eq "OK"){$SaveFileDialog.FileName}
}
ParameterSets : {#{Name=__AllParameterSets; IsDefault=False; Parameters=System.Management.Automation.PSObject[]}}