Good evening.
I'm trying to use parallelism for the first time but I don't understand how to call a function within foreach loop.
I get a series of this error: Cannot bind argument to parameter 'Path' because it is null.
This is what I've done so far:
$FolderPath = "C:\myfolder\"
function AppendLog ($client) {
$so = New-CimSessionOption -Protocol 'DCOM'
$s = New-CimSession -ComputerName $client -SessionOption $so
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -PING: OK")
$arch = Get-CimInstance –query "select * from win32_operatingsystem" -CimSession $s | select -expandproperty osarchitecture
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -ARCH:" + $arch )
$lastboot = Get-CimInstance –query "select * from win32_operatingsystem" -CimSession $s | select -expandproperty lastbootuptime
Add-Content -Path (join-path $folderpath "LOGS.txt") -Value ( (get-date -Format "[yyyy.MM.dd HH:mm:ss]").tostring() + $client + " -BOOT:" + $lastboot )
}
$funcDef = $function:AppendLog.ToString()
$clients = get-content -path (join-path $folderPath "client.txt")
$clients | ForEach-Object -parallel {
if (test-connection $_ -count 2 -Quiet)
{
$function:AppendLog = $using:funcDef
AppendLog ($_)
}
} -throttlelimit 3
Could you explain me how to pass my path?
My bad on the comment, the error you're getting is most likely coming from your function. The error is being thrown by Join-Path
:
PS /> Join-Path $null 'Logs.txt'
Join-Path : Cannot bind argument to parameter 'Path' because it is null.
At line:1 char:11
+ Join-Path $null 'Logs.txt'
+ ~~~~~
+ CategoryInfo : InvalidData: (:) [Join-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCommand
The reason is because $FolderPath
doesn't exist in the scope of your parallel loop. $folderpath
should be replace with $using:folderpath
inside your function.
As a side note, adding information to the same file on a parallel execution doesn't seem to be a good idea.
Last edit, I understand if this is meant to test how ForEach-Object -Parallel
works but again, if the cmdlet allows remote querying / remote execution with multiple hosts at the same time, let the cmdlet handle that for you, it is more efficient.
As for the code, this is what I would use with what you already have:
$FolderPath = "C:\myfolder\"
$sessionOption = New-CimSessionOption -Protocol 'DCOM'
$clients = Get-Content -Path (Join-Path $FolderPath -ChildPath "Client.txt")
$results = $clients | ForEach-Object -Parallel {
$out = @{
Time = [datetime]::Now.ToString('[yyyy.MM.dd HH:mm:ss]')
ComputerName = $_
}
if ($ping = Test-Connection $_ -Count 2 -Quiet)
{
$session = New-CimSession -ComputerName $_ -SessionOption $using:sessionOption
$OSInfo = Get-CimInstance -CimSession $session -ClassName win32_operatingSystem
Remove-CimSession $session
}
$out.Ping = $ping
$out.Arch = $OSInfo.OSArchitecture
$out.LastBoot = $OSInfo.LastBootUpTime
[pscustomobject]$out
} -ThrottleLimit 3
$results | Export-Csv "$myFolder\LOGS.csv" -NoTypeInformation
This will output an object like this below:
Time ComputerName Ping OSArchitecture LastBoot
---- ------------ ---- -------------- --------
[2021.06.19 20:06:00] ComputerName01 True 64-bit 6/16/2021 11:47:16 AM
[2021.06.19 20:07:00] ComputerName02 False
[2021.06.19 20:08:00] ComputerName03 True 64-bit 6/13/2021 11:47:16 AM
[2021.06.19 20:09:00] ComputerName04 True 64-bit 6/14/2021 11:47:16 AM
[2021.06.19 20:10:00] ComputerName05 True 64-bit 6/15/2021 11:47:16 AM
Which can be exported nicely to a CSV instead of a text file. P.D.: sorry for the syntax highlighting :(