Search code examples
powershellrunspace

Add-PSsnapin in runspace with foreach-parallel


I use the funtion foreach-parallel for run many scriptblock like runspace the first load snapin correctly but all next return 2 error

1..5 | Foreach-Parallel -Throttle 5 -Timeout 5 -sleeptimer 5 {
   add-PSSnapin 'Quest.ActiveRoles.ADManagement'
    try {
        get-qadUser 'Domain\me'
    } catch {
        return "$($Error[0].Exception)"
    }
}

my errors is :

add-PSSnapin : An item with the same key has already been added

get-qadUser : function doesn't exist


Solution

  • Finaly I use another code, I use like that :

    $Servers | Run-Parallel -scriptBlock $ScriptBlock -TimeOut 10 -PSModules 'PSTerminalServices' -PSSnapins 'Quest.ActiveRoles.ADManagement' | out-gridView
    

    complet code

        function Run-Parallel {
            <#
                .Synopsis
                    This is a quick and open-ended script multi-threader searcher
                    http://www.get-blog.com/?p=189#comment-28834
                    Improove by Alban LOPEZ 2016
    
                .Description
                    This script will allow any general, external script to be multithreaded by providing a single
                    argument to that script and opening it in a seperate thread.  It works as a filter in the 
                    pipeline, or as a standalone script.  It will read the argument either from the pipeline
                    or from a filename provided.  It will send the results of the child script down the pipeline,
                    so it is best to use a script that returns some sort of object.
    
                    Authored by Ryan Witschger - http://www.Get-Blog.com
    
                .PARAMETER ScriptBlock
                    This is where you provide the PowerShell ScriptBlock that you want to multithread.  
    
                .PARAMETER ItemObj
                    The ItemObj represents the arguments that are provided to the child script.  This is an open ended
                    argument and can take a single object from the pipeline, an array, a collection, or a file name.  The 
                    multithreading script does it's best to find out which you have provided and handle it as such.  
                    If you would like to provide a file, then the file is read with one object on each line and will 
                    be provided as is to the script you are running as a string.  If this is not desired, then use an array.
    
                .PARAMETER InputParam
                    This allows you to specify the parameter for which your input objects are to be evaluated.  As an example, 
                    if you were to provide a computer name to the Get-Process cmdlet as just an argument, it would attempt to 
                    find all processes where the name was the provided computername and fail.  You need to specify that the 
                    parameter that you are providing is the "ComputerName".
    
                .PARAMETER AddParam
                    This allows you to specify additional parameters to the running command.  For instance, if you are trying
                    to find the status of the "BITS" service on all servers in your list, you will need to specify the "Name"
                    parameter.  This command takes a hash pair formatted as follows:  
    
                    @{"key" = "Value"}
                    @{"key1" = "Value"; "key2" = 321; "key3" = 1..9}
    
                .PARAMETER AddSwitch
                    This allows you to add additional switches to the command you are running.  For instance, you may want 
                    to include "RequiredServices" to the "Get-Service" cmdlet.  This parameter will take a single string, or 
                    an aray of strings as follows:
    
                    "RequiredServices"
                    @("RequiredServices", "DependentServices")
    
                .PARAMETER MaxThreads
                    This is the maximum number of threads to run at any given time.  If resources are too congested try lowering
                    this number.  The default value is 20.
    
                .PARAMETER SleepTimer
                    This is the time between cycles of the child process detection cycle.  The default value is 200ms.  If CPU 
                    utilization is high then you can consider increasing this delay.  If the child script takes a long time to
                    run, then you might increase this value to around 1000 (or 1 second in the detection cycle).
    
                .PARAMETER TimeOut
                    this is the timeOut for slower instance, only each other are returned
    
                .PARAMETER PSModules
                    List of PSModule name to include for use in ScriptBlock
    
                .PARAMETER PSSapins
                    List of PSSapin name to include for use in ScriptBlock
    
                .EXAMPLE
                    Both of these will execute the scriptBlock and provide each of the server names in AllServers.txt
                    while providing the results to GridView.  The results will be the output of the child script.
    
                    gc AllServers.txt | Run-Parallel $ScriptBlock_GetTSUsers -MaxThreads $findOut_AD.ActiveDirectory.Servers.count -PSModules 'PSTerminalServices' | out-gridview
            #>
            Param(
                [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
                    $ItemObj,
                [ScriptBlock]$ScriptBlock = $null, 
                $InputParam = $Null,
                [HashTable] $AddParam = @{},
                [Array] $AddSwitch = @(),
                $MaxThreads = 20,
                $SleepTimer = 200,
                $TimeOut = 5,
                [string[]]$PSSapins = $null,
                [string[]]$PSModules = $null
            )
            Begin{
                $ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
                ForEach ($Snapin in $PSSapins){
                    [void]$ISS.ImportPSSnapIn($Snapin, [ref]$null)
                }
                ForEach ($Module in $PSModules){
                    [void]$ISS.ImportPSModule($Module)
                }
                $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host)
                $RunspacePool.Open()
                $Jobs = @()
                if ($CommonObject.Settings.Debug) {$CommonObject.host.ui.WriteLine("[  Start  ] :")}
            }
            Process{
                Write-Progress -Activity "Preloading Threads" -Status "Starting Job $($jobs.count)"
                #ForEach ($Object in $ItemObj){
                    if ($ItemObj){
                        $PowershellThread = [powershell]::Create().AddScript($ScriptBlock)
    
                        If ($InputParam -ne $Null){
                            $PowershellThread.AddParameter($InputParam, $ItemObj.ToString()) | out-null
                        }Else{
                            $PowershellThread.AddArgument($ItemObj.ToString()) | out-null
                        }
                        ForEach($Key in $AddParam.Keys){
                            $PowershellThread.AddParameter($Key, $AddParam.$key) | out-null
                        }
                        ForEach($Switch in $AddSwitch){
                            $PowershellThread.AddParameter($Switch) | out-null
                        }
                        $PowershellThread.RunspacePool = $RunspacePool
                        $Handle = $PowershellThread.BeginInvoke()
                        $Job =  [pscustomobject][ordered]@{Handle=''; Thread=''; object=''}
                        $Job.Handle = $Handle
                        $Job.Thread = $PowershellThread
                        $Job.Object = $ItemObj.ToString()
                        $Jobs += $Job
                        if ($CommonObject.Settings.Debug) {$CommonObject.host.ui.WriteLine("`t$ItemObj")}
                    }
                #}
            }
            End{
                $ResultTimer = Get-Date
                While (@($Jobs | Where-Object {$_.Handle -ne $Null}).count -gt 0)  {
    
                    $Remaining = "$($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).object)"
                    If ($Remaining.Length -gt 60){
                        $Remaining = $Remaining.Substring(0,60) + "..."
                    }
                    Write-Progress `
                        -Activity "Waiting for Jobs - $($MaxThreads - $($RunspacePool.GetAvailableRunspaces())) of $MaxThreads threads running" `
                        -PercentComplete (($Jobs.count - $($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).count)) / $Jobs.Count * 100) `
                        -Status "$(@($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False})).count) remaining - $remaining" 
    
                    ForEach ($Job in $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $True})){
                        $Job.Thread.EndInvoke($Job.Handle)
                        $Job.Thread.Dispose()
                        $Job.Thread = $Null
                        $Job.Handle = $Null
                        $ResultTimer = Get-Date
                    }
                    If (($(Get-Date) - $ResultTimer).totalseconds -gt $TimeOut){
                        $NOK = $($Jobs | Where-Object {$_.Handle})
                        if ($CommonObject.Settings.Debug) {$CommonObject.host.ui.WriteErrorLine("[ TimeOut ] $($NOK.count) : $($NOK.object)")}
                        $NOK | %{
                            if ($CommonObject.Settings.Debug) {$CommonObject.host.ui.WriteErrorLine("[ stop ] $($_.object)")}
                            $_.thread.Stop() | Out-Null
                            if ($CommonObject.Settings.Debug) {$CommonObject.host.ui.WriteErrorLine("[ dispose ] $($_.object)")}
                            $_.thread.Dispose()
                            if ($CommonObject.Settings.Debug) {$CommonObject.host.ui.WriteErrorLine("[ null ] $($_.object)")}
                            $_.Thread = $Null
                            $_.Handle = $Null
                        }
                        $RunspacePool.Close() | Out-Null
                        $RunspacePool.Dispose() | Out-Null
                        exit
                    }
                    Start-Sleep -Milliseconds $SleepTimer
                } 
                $RunspacePool.Close() | Out-Null
                $RunspacePool.Dispose() | Out-Null
            } 
        }