使用foreach-parallel

时间:2016-03-11 17:23:22

标签: powershell runspace

我使用功能foreach-parallel运行许多脚本块,如运行空间 第一次加载管道正确,但下一次返回2错误

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)"
    }
}

我的错误是:

  

add-PSSnapin:已添加具有相同密钥的项目

     

get-qadUser:函数不存在

4 个答案:

答案 0 :(得分:1)

在runspacePool中,命令Add-PSSnapinImport-Module不起作用! 它需要在ScriptBlock中添加初始默认状态:

    $ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault()
    [void]$ISS.ImportPSSnapIn('Quest.ActiveRoles.ADManagement', [ref]$null)
    [void]$ISS.ImportPSModule('PSTerminalServices')
    $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host)
    $RunspacePool.Open()

答案 1 :(得分:0)

我在上面就我认为你应该做的事情做了评论,但澄清你要确保在你的电话之前加载了snapin,所以确保在你运行Foreach-parallel之前执行它。

add-PSSnapin 'Quest.ActiveRoles.ADManagement'

Foreach-Parallel -Throttle 5 -Timeout 5 -sleeptimer 5 {
   get-qadUser 'domain\testUser'
}

答案 2 :(得分:0)

有一种简单的方法可以测试cmdlet,如果没有找到,请添加PSSnapin或Import-Module

将以下内容添加到脚本的开头将检查是否存在CMDLET,如果没有找到,请添加它。

IF(
 Get-Command get-qadUser -ErrorAction SilentlyContinue){}
ELSE{add-PSSnapin 'Quest.ActiveRoles.ADManagement'}

答案 3 :(得分:0)

最后我使用另一个代码,我这样使用:

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

complet代码

    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
        } 
    }