Назад | Перейти на главную страницу

Как получить данные из задания пространства выполнения powershell

Пытаюсь выполнить несколько сценариев в многопоточном режиме, выполнение которых требует времени. Один из примеров - получение последнего входа для пользователя. Он проверяет все наши DC, а затем возвращает самое последнее время. У нас их довольно много, и они глобальны, поэтому последовательная работа требует времени.

Я видел этот ответ Как мне запустить мои сценарии PowerShell параллельно без использования заданий?

что заставило меня настроить пространство выполнения и запустить его, но я не уверен, как вернуть данные.

Это то, что у меня есть

$username = Read-Host "Enter the Users ID"
$dcs = Get-ADDomainController -Filter {Name -like "*"} | Select -expandproperty name
$Code = {
  Param($username,$dc)
  Get-ADUser $username | Get-ADObject -Server $dc -Properties lastLogon | 

Select -Expandproperty lastLogon
  }
$rsPool = [runspacefactory]::CreateRunspacePool(1,8)
$rsPool.Open()
foreach($dc in $dcs)
  {
  $PSinstance = [powershell]::Create().AddScript($Code).AddArgument($username).AddArgument($dc)
  $PSinstance.RunspacePool = $RunspacePool
  $PSinstance.BeginInvoke()
  }

Поэтому мне просто нужно дождаться завершения каждой работы, а затем фиксировать результаты каждой, а я не уверен, как это сделать.

Изменить: также я ранее пытался сделать это с заданиями, но код на самом деле занимал больше времени, чем обычный скрипт

$userName = Read-Host "Enter NTID: "

$time = 0


$dcs = Get-ADDomainController -Filter {Name -like "*"} | Select -expandproperty name


$scriptbox = {

Param($username,$dc)

Get-ADUser $username | Get-ADObject -Server $dc -Properties lastLogon | Select -Expandproperty lastLogon

}   

foreach($dc in $dcs){start-Job -ScriptBlock $scriptbox -ArgumentList $username,$dc}


Get-Job | Wait-Job


Get-Job

$Data = ForEach ($Job in (Get-Job)) {

Receive-Job $Job

Remove-Job $Job

}


Foreach ($date in $Data){if($date -gt $time){$time = $date}}


$dt = [DateTime]::FromFileTime($time)

write-Host $username "last logged on at:" $dt 

Вы правы, используя Runspaces вместо *-Job командлеты, поскольку они намного быстрее!

Я недавно опубликовал AsyncTcpScan который использует Runspaces и работает невероятно быстро! Его можно легко изменить для запуска любого блока сценария. Ниже показано, как должен выглядеть ваш исходный сценарий после интеграции.

ВНИМАНИЕ: мне не удалось протестировать следующий код. Я внес некоторые изменения в ваш исходный сценарий, основываясь на моем опыте работы с командлетами Active Directory.

# Script to run in each thread.
[System.Management.Automation.ScriptBlock]$ScriptBlock = {

    $adUser = Get-ADUser -Identity $args[0] -Server $args[1] -Properties lastLogon

    $result = New-Object PSObject -Property @{ 'User'      = $args[0];
                                               'DC'        = $args[1];
                                               'LastLogon' = $adUser.LastLogon; }

    return $result

} # End Scriptblock

function Invoke-AsyncJob
{
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [System.String]
        # User ID
        $Username
    )

    Import-Module -Name ActiveDirectory

    $Results = @()

    $AllJobs = New-Object System.Collections.ArrayList

    $AllDomainControllers = Get-ADDomainController -Filter "*"

    $HostRunspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool(2,10,$Host)

    $HostRunspacePool.Open()

    foreach($DomainController in $AllDomainControllers)
    {
        $asyncJob = [System.Management.Automation.PowerShell]::Create().AddScript($ScriptBlock).AddParameters($($Username,$($DomainController.Name)))

        $asyncJob.RunspacePool = $HostRunspacePool

        $asyncJobObj = @{ JobHandle   = $asyncJob;
                          AsyncHandle = $asyncJob.BeginInvoke()    }

        $AllJobs.Add($asyncJobObj) | Out-Null
    }

    $ProcessingJobs = $true

    Do {

        $CompletedJobs = $AllJobs | Where-Object { $_.AsyncHandle.IsCompleted }

        if($null -ne $CompletedJobs)
        {
            foreach($job in $CompletedJobs)
            {
                $result = $job.JobHandle.EndInvoke($job.AsyncHandle)

                if($null -ne $result)
                {
                    $Results += $result
                }

                $job.JobHandle.Dispose()

                $AllJobs.Remove($job)
            } 

        } else {

            if($AllJobs.Count -eq 0)
            {
                $ProcessingJobs = $false

            } else {

                Start-Sleep -Milliseconds 1000
            }
        }

    } While ($ProcessingJobs)

    $HostRunspacePool.Close()
    $HostRunspacePool.Dispose()

    return $Results

} # End function Invoke-AsyncJob