invoke-parallel -scriptblock的效率和速度增加

时间:2017-02-11 16:15:18

标签: python windows python-3.x powershell powershell-v4.0

是否有方法可以加快此命令并使其更有效地使用资源?

如果不必提取文件并且可以通过evtx文件格式解析,我愿意转换为python 3.5。

我有一个运行cmdlet的脚本,调用并行脚本块。 $ files是我根据用户名和事件ID过滤的10000个evtx文件的列表。

我试图调用powershell.exe的新实例并从scriptblock运行get-winevent cmdlet,但这会实例化到许多进程而不会关闭。我没有尝试过工作,但我不确定在这种情况下该做什么。

$files | Invoke-Parallel -ImportModules -ScriptBlock{ 
Get-WinEvent -FilterHashtable @{Path=$_;id=4624;data="ANONYMOUS LOGON","user.name1", "user.name2" } | 
    Select-Object  -Property MachineName,RecordId, TimeCreated,Id,
    @{Name="SubjectUserSid"; Expression={$_.Properties[0].Value}},
    @{Name="SubjectUserName";Expression={$_.Properties[1].Value}},
    @{Name="SubjectDomainName";Expression={$_.Properties[2].Value}},
    @{Name="SubjectLogonId";Expression={$_.Properties[3].Value}},
    @{Name="TargetUserSid";Expression={$_.Properties[4].Value}},
    @{Name="TargetUserName"; Expression={$_.Properties[5].Value}},
    @{Name="TargetDomainName";Expression={$_.Properties[6].Value}},
    @{Name="TargetLogonId";Expression={$_.Properties[7].Value}},
    @{Name="LogonType";Expression={$_.Properties[8].Value}},
    @{Name="LogonProcessName";Expression={$_.Properties[9].Value}},
    @{Name="AuthenticationPackageName";Expression={$_.Properties[10].Value}},
    @{Name="WorkstationName";Expression={$_.Properties[11].Value}},
    @{Name="LogonGuid";Expression={$_.Properties[12].Value}},
    @{Name="TransmittedServices";Expression={$_.Properties[13].Value}},
    @{Name="LmPackageName";Expression={$_.Properties[14].Value}},
    @{Name="KeyLength";Expression={$_.Properties[15].Value}},
    @{Name="ProcessId";Expression={$_.Properties[16].Value}},
    @{Name="ProcessName";Expression={$_.Properties[17].Value}},
    @{Name="IP"; Expression={$_.Properties[18].Value}},
    @{Name="IpPort";Expression={$_.Properties[19].Value}}} -throttle 100 |
     Export-Csv -path "C:\users\username\Desktop\folder\full.csv"   

2 个答案:

答案 0 :(得分:1)

您可以考虑设置Runspaces and RunspacePools。它们设置起来有点棘手,但它们的工作方式非常酷且效率很高。池允许您设置各种节流。 RunspaceFactory将作业加载到x个槽中的1个槽中,然后一旦完成,它就会在该槽中抛出另一个槽。 其中一个重大优势是开销 - 或者缺乏开销。 Runspaces不需要另一个PowerShell实例,而Job则需要。

因此,如果您有一个宽度为50的RunspacePool和1000个要处理的作业,那么其中50个作业将在任何时间运行。超酷。

Check out this example from mjolinor。 当然,这是一个非常复杂的例子。他经历了很多麻烦,无法访问各种数据流。这样就可以监控池中的进程。

答案 1 :(得分:1)

以下是在我的主安全审计事件日志文件(20MB)上测量的单个作业的~50倍加速。

您的代码问题是由标准的PowerShell引起的,这在大量数据的情况下非常低效。

  • 具有20个计算属性的Select-Object为每条记录创建20个ScriptBlock上下文。与内部的实际简单代码相比,PS中的上下文创建需要花费大量时间。

  • Get-WinEvent为每个事件创建自定义对象,其中包含20多个NoteProperty对象,
    每个都需要时间来创建。

  • 除了使用慢速PS管道外,Export-CSV还需要访问每个NoteProperty

  • |流水线操作比流控制语句(如foreach(非cmdlet))慢,而。

让我们变脏并使用.NET 3.5+和PS3 +手动完成所有事情:

$CollectLogonsInCsv = {
param(
    [ValidateScript({ Test-Path -literal $_ })]
    [string]$eventLogPath,

    [Parameter(Mandatory)]
    [string[]]$users,

    [ValidateScript({ Test-Path -IsValid -literal $_ })]
    [string]$outputPath = ($eventLogPath -replace '[^.]+$', 'csv')
)
    $query = '*[System/EventID=4624 and EventData[' +
        ($users -replace '^.+', 'Data[@Name="TargetUserName"]="$&"' -join ' or ') + ']]'
    $reader = [Diagnostics.Eventing.Reader.EventLogReader]::new(
        [Diagnostics.Eventing.Reader.EventLogQuery]::new($eventLogPath,
            [Diagnostics.Eventing.Reader.PathType]::FilePath, $query)
    )
    $writer = [IO.StreamWriter]::new($outputPath, $false, [Text.Encoding]::UTF8, 16MB)
    $writer.WriteLine('MachineName, RecordId, TimeCreated, Id,' +
        'SubjectUserSid, SubjectUserName, SubjectDomainName, SubjectLogonId, ' +
        'TargetUserSid, TargetUserName, TargetDomainName, TargetLogonId, ' +
        'LogonType, LogonProcessName, AuthenticationPackageName, WorkstationName, ' +
        'LogonGuid, TransmittedServices, LmPackageName, KeyLength, ' +
        'ProcessId, ProcessName, IP, IpPort')
    while ($e = $reader.ReadEvent()) {
        $p = $e.properties
        $writer.WriteLine('"' +
            [string]::Join("`0",
                $($e.MachineName, $e.RecordId, $e.TimeCreated, $e.Id; $p[0..19].value)
            ).replace('"', '""').replace("`0", '","') + '"'
        )
    }
    $writer.close()
    [GC]::Collect()
}

现在使用RunSpaces并行调用它以进一步改进:

$outputDir = 'C:\Users\Administrator\Desktop\folder\4768'

Get-Content C:\users\Administrator\Desktop\fullfiles.csv |
    Invoke-Parallel -throttle 100 -ImportModules -ImportVariables -ScriptBlock {
        $outputCsv = Join-Path $outputDir ((Get-Item -literal $_).BaseName + '.csv')
        & $CollectLogonsInCsv $_ @(
            'user.name.1'
            'user.name.2'
            'user.name.3'
            'user.name.4'
        ) $outputCsv
    }