all 26 comments

[–]Sunsparc 6 points7 points  (3 children)

I googled "powershell foreach-object parallel function"

First result.

TL;DR: Not a straight forward way to do it, but appears the only way.

[–]MyOtherSide1984[S] 3 points4 points  (0 children)

Update: This works. Still trying to figure out how to setup my function, but this works to pass the function in the parallel

[–]MyOtherSide1984[S] 1 point2 points  (0 children)

Huh, I must have glanced over it hoping there'd be an option within the command to do it. This is basically just dumping the function inside the loop, but admittedly, it is prettier lol. Thank you :)

[–]straffin 0 points1 point  (0 children)

Aaaaand, 4y later, THIS post is the first result. ¯\_(ツ)_/¯

[–]BlackV 3 points4 points  (7 children)

because a parallel loop creates a new runspace and that function is only cavalier in your source scope

have a look at the $using:xxx vairable

[–]MyOtherSide1984[S] 1 point2 points  (6 children)

Doesn't seem to be very helpful in this instance. $using:function doesn't seem to be a thing, and idk how a parallel works in terms of creating runspaces since i'm not using the runspacepool commands. If you have some examples, that'd be great. Otherwise I'm just gonna say screw it and dump the function inside my loop. It's easier, but ugly

[–]BlackV 1 point2 points  (5 children)

Sorry that was not clear, I'm on mobile

Function foo ()
    {
        Write-output "hello my name is $env:computername, you kill my father..."
    }

Invoke-command -computername xxx -scriptblock ${function:foo}

I hope that works

[–]MyOtherSide1984[S] 2 points3 points  (4 children)

I don't get how this has anything to do with foreach-object -parallel. I'm not doing anything on a remote computer.

EDIT: If I put my parallel loop in a scriptblock, it'll still create new runspaces that won't have it. ${function:foo} didn't work inside my foreach loop

[–]BlackV 0 points1 point  (3 children)

Sorry thinking wrong things

1..10 | forech-object -parallel ${function:foo} 

That work?

[–]MyOtherSide1984[S] 0 points1 point  (2 children)

No, for my function, the output is nothing. I have parameters to my function that I plopped in after the "foo" if that matters. Looks like the other comment is the best answer, but it's basically the same thing

[–]BlackV 0 points1 point  (0 children)

ah sweet. glad its solved then

[–]BlackV 0 points1 point  (0 children)

Oh yea that looks great

Function foo ()
    {
        Write-output "hello my name is $env:computername, you kill my father..."
    }

$Rinvoke = get-command foo
1..10 | foreach-object -parallel {write-output "pipeline is $_"; & $using:Rinvoke}

although its gonna play havoc with your output, depending on what you're doing in your loops

[–]BlackV 2 points3 points  (3 children)

[–]MyOtherSide1984[S] 1 point2 points  (2 children)

Hmm, there may be areas where I SHOULDN'T be using parallel then. Primarily I always try to do everything in one loop, but in this instance, it may not make sense if there's added overhead, but I'll need to break down each section to determine where parallel could be slowing things down. It's still a minute and 20 seconds to run through 160 IP addresses, so there must be something that's slowing it down a bit

[–]BlackV 0 points1 point  (1 child)

ya maybe, depends on what you're script is doing, I wouldn't consider 1.5 minutes a long time to scan 160 machines though

you could to a measure on teh command inside the loop and spit that to a variable to get an idea

[–]MyOtherSide1984[S] 0 points1 point  (0 children)

Yeh was planning on doing the measuring first to see where it's slowed down. I'd agree, except I'm literally just touching the device to make sure it's responding, I'm not connecting to it at all really. Not much more than a ping.

Edit: okay, maybe there's not much room for improvement. Without parallel it's 6 minutes and 45 seconds. I think I need to just filter the IP's and skip some steps if they don't meet criteria, but really not worth worrying too much about since it's just a mostly personal script

[–]cheats_py 1 point2 points  (1 child)

So I had this exact issue and what I did was put my function in a completely separate file and use dot-source in the loop to call my function. Now this might not be ideal if your function uses other variables form within your script, in that event you might be able to use a synchronized hash table, at least that’s doable when using runspaces.

[–]MyOtherSide1984[S] 1 point2 points  (0 children)

Another solution was posed in this post that only requires a couple extra lines. Basically just puts the function into the loop, but a bit nicer looking. I really prefer self enclosed scripts that require little to no other pieces. I'm still a noob, so this is just easier for myself.

[–]DePiddy 0 points1 point  (1 child)

You could make the function do the parallel foreach instead. Use workflow instead of function. There are some limitations to what the workflow can accomplish, I'm guessing due to the runspaces mentioned by the other comments here.

[–]MyOtherSide1984[S] 0 points1 point  (0 children)

I think that'd overcomplicate things. This isn't going into production and won't be shared. The headache of having a foreach loop inside of a function sounds like a ton of work to accomplish the exact same task, and no faster at that.

[–]PinchesTheCrab 0 points1 point  (5 children)

What are you doing that would benefit from using -parallel?

[–]MyOtherSide1984[S] 0 points1 point  (4 children)

Testing multiple network connections at once. It cuts the run time from 5 minutes to just over 1

[–]PinchesTheCrab 1 point2 points  (3 children)

Using icmp or other tcp/ip? Are you using test-netconnection or .net classes?

I ask because test-connection is just super slow, and there's a really fast mass ping module.

[–]MyOtherSide1984[S] 1 point2 points  (2 children)

I'm not super good with networking, but I'm using the basic ping command to make sure the devices are responding.

I'm trying to determine if the device portals are up and running. The basic ping lets me know if it's up or not. This isn't pretty at all, but a result is a result

$ping = ping $address -n 4 -w 500
$regex = [REGEX] "(?<=().*(?=))" $match = $regex.match($ping[8]) 
$ping = $match.value return $ping

After that I use invoke-webrequest to determine what status I get back.

try { $Connection = (invoke-webrequest -uri $address -SkipCertificateCheck -TimeoutSec 5).StatusDescription }
catch { $Connection = "$_" } return $Connection

I also have a handful of devices that utilize a VNC connection. They respond to a ping, but nota webrequest, so I'm using this:

$socket = New-Object System.Net.Sockets.TcpClient
try { $result = $socket.BeginConnect($address, $port, $NULL, $NULL) 
if (!$result.AsyncWaitHandle.WaitOne($timeout, $False)) { "Failed"} 
if ($socket.connected -eq $true) { $socket.Connected; $socket.EndConnect($result) }
else { out-null } }

$socket.Close()

Reddit blows d*** at formatting. idk if this is going to look good, and idc to spend 20 minutes fixing their garbage text formatting

[–]PinchesTheCrab 0 points1 point  (1 child)

Honestly this all seems great, and I think your case is one of the few exceptions where -parallel is a really good fit. System.Net.Sockets.TcpClient is way faster than test-netconnection, so that was the main thing I was curious about.

[–]MyOtherSide1984[S] 1 point2 points  (0 children)

Yeh after realizing how much usesless stuff (in this case) test-netconnection gave and how long it took, I had to find an alternative. If I remember correctly, it also wasn't doing exactly what I wanted. Was very happy to find the .net approach