Add-PSsnapin в среде выполнения с foreach-parallel

Я использую функцию 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: функция не существует


person Alban    schedule 11.03.2016    source источник
comment
Вам нужно будет добавить оснастку только один раз в сеанс хоста. Переместите Add-PSSnappin за пределы цикла foreach.   -  person ATek    schedule 11.03.2016


Ответы (4)


В runspacePool команда Add-PSSnapin и Import-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()
person Alban    schedule 15.03.2016

Я сделал комментарий выше о том, что, по моему мнению, вы должны сделать, но чтобы уточнить, вы захотите убедиться, что оснастка загружена до вашего вызова, поэтому убедитесь, что она выполнена, прежде чем запускать Foreach-parallel.

add-PSSnapin 'Quest.ActiveRoles.ADManagement'

Foreach-Parallel -Throttle 5 -Timeout 5 -sleeptimer 5 {
   get-qadUser 'domain\testUser'
}
person ATek    schedule 11.03.2016
comment
foreach-parallel не является родной функцией powershell. он создает пространство выполнения для запуска блока сценария. и квест недоступен в ранспейсе - person Alban; 14.03.2016

Существует простой способ проверить наличие командлета и, если он не найден, добавить PSSnapin или Import-Module

Добавление приведенного ниже кода в начало вашего скрипта проверит наличие CMDLET и, если он не найден, добавит его.

IF(
 Get-Command get-qadUser -ErrorAction SilentlyContinue){}
ELSE{add-PSSnapin 'Quest.ActiveRoles.ADManagement'}
person user4317867    schedule 11.03.2016
comment
Хорошо, я знаю этот метод, но первый случай работает правильно. а в следующем ошибка - person Alban; 14.03.2016
comment
моя ошибка на французском языке, немного перевести в моем первом посте. - person Alban; 15.03.2016

Наконец, я использую другой код, я использую так:

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

полный код

    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
        } 
    }
person Alban    schedule 04.04.2016