0

I was very recently introduced to the concepts of Runspaces. After several YouTube videos and forum posts I get the idea as to how they work, but I struggle when trying to include it in a script of my own.

if i have the code to implement one:

(thanks to https://blog.netnerds.net/2016/12/runspaces-simplified/)

# BLOCK 1: Create and open runspace pool, setup runspaces array with min and max threads
$pool = [RunspaceFactory]::CreateRunspacePool(1, [int]$env:NUMBER_OF_PROCESSORS+1)
$pool.ApartmentState = "MTA"
$pool.Open()
$runspaces = $results = @()

# BLOCK 2: Create reusable scriptblock. This is the workhorse of the runspace. Think of it as a function.
$scriptblock = {
    Param (
    [string]$connectionString,
    [object]$batch,
    [int]$batchsize
    )

    $bulkcopy = New-Object Data.SqlClient.SqlBulkCopy($connectionstring,"TableLock")
    $bulkcopy.DestinationTableName = "mytable"
    $bulkcopy.BatchSize = $batchsize
    $bulkcopy.WriteToServer($batch)
    $bulkcopy.Close()
    $dtbatch.Clear()
    $bulkcopy.Dispose()
    $dtbatch.Dispose()

    # return whatever you want, or don't.
    return $error[0]
}

# BLOCK 3: Create runspace and add to runspace pool
if ($datatable.rows.count -eq 50000) {

    $runspace = [PowerShell]::Create()
    $null = $runspace.AddScript($scriptblock)
    $null = $runspace.AddArgument($connstring)
    $null = $runspace.AddArgument($datatable)
    $null = $runspace.AddArgument($batchsize)
    $runspace.RunspacePool = $pool

# BLOCK 4: Add runspace to runspaces collection and "start" it
    # Asynchronously runs the commands of the PowerShell object pipeline
    $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
    $datatable.Clear()
}

# BLOCK 5: Wait for runspaces to finish
 while ($runspaces.Status.IsCompleted -notcontains $true) {}

# BLOCK 6: Clean up
foreach ($runspace in $runspaces ) {
    # EndInvoke method retrieves the results of the asynchronous call
    $results += $runspace.Pipe.EndInvoke($runspace.Status)
    $runspace.Pipe.Dispose()
}

$pool.Close() 
$pool.Dispose()

# Bonus block 7
# Look at $results to see any errors or whatever was returned from the runspaces

And a script I want to include it in:

$OUs = "OU=Terminated,OU=####,OU=####,DC=####,DC=####"

foreach ($ou in $OUs) 
{
  $users = Get-ADUser -SearchBase $ou -Filter * 
  foreach ($user in $users)
  {
      $groups = Get-ADPrincipalGroupMembership -Identity $User | ? {$_.distinguishedName -like "*Groups I want removed*" }
      foreach($group in $groups) 
      {
         Remove-ADPrincipalGroupMembership -Identity $user -MemberOf $group -whatif    
      }
  }
}

$results = foreach ($OU in $OUs) 
{
    get-aduser -SearchBase $OU -filter * -Properties MemberOf | ? MemberOf -like "*Distribution Lists*"
}
$results | Export-Csv .\Remainingusers.csv -NoTypeInformation

How would I merge these together? from what I'm reading and seeing I just drop 1 into the beginning of 2 however when I do that the script docent seem to run at all and just looks frozen. I know I'm doing something wrong, but I don't know what.

Splicing My script in between blocks 4 and 5 lets it work, but I don't notice it being any faster.

mklement0
  • 382,024
  • 64
  • 607
  • 775
BPengu
  • 92
  • 8
  • You are copy pasting code from other sites. What have you tried yourself to create workflow for parallel executions? – Jawad Jan 16 '20 at 15:38
  • I wouldn't of, this is all new to me. – BPengu Jan 16 '20 at 15:59
  • 1
    Take the part of you code that you want to run in parallel (ie. `Remove-ADPrincipalGroupMembership ...`) and put that in the `$scriptblock` in block 2 of the sample script – Mathias R. Jessen Jan 16 '20 at 17:52
  • 2
    if you find runspaces difficult - and i certainly do - i recommend you look into the `PoShRSJob` module for a very nice wrapper around runspaces that works much like standard PoSh jobs work. – Lee_Dailey Jan 16 '20 at 18:30

0 Answers0