Пытаюсь выполнить несколько сценариев в многопоточном режиме, выполнение которых требует времени. Один из примеров - получение последнего входа для пользователя. Он проверяет все наши 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