python - effektivitet og hastighedsforøgelse af invoke-parallel-scriptblock

Indlæg af Hanne Mølgaard Plasc

Problem



Er der en metode til at fremskynde denne kommando og gøre det mere effektivt at bruge ressourcer?


Jeg er villig til at konvertere til python 3.5, hvis filerne ikke skal udvindes og kan analyseres via evtx filformat.


Jeg har et script, jeg kører med cmdleten, der er parallel med en scriptblok. $ Filerne er en liste over 10000 evtx filer jeg filtrerer baseret på brugernavn og begivenhed ids.


Jeg har forsøgt at kalde en ny forekomst af powershell.exe og køre cmdlet get-winevent fra scriptblocket, men dette instanser til mange processer uden at lukke ud. Jeg har ikke prøvet job, men er i tvivl om hvor jeg skal gøre det i dette tilfælde.


$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:usersusernameDesktopfolderfull.csv"   

Bedste reference


Nedenfor er en ~ 50x speed up af single job som målt på min vigtigste sikkerhed audit eventlog fil (20MB).


Problemet med din kode er forårsaget af standard PowerShell-stuff, der er forfærdeligt ineffektivt i tilfælde af store mængder data.



  • Vælg-objekt med 20 beregnede egenskaber opretter 20 ScriptBlock-sammenhænge for hver post. Kontekst oprettelse i PS tager meget tid i forhold til den faktiske enkle kode inde.

  • Get-WinEvent opretter brugerdefinerede objekter for hver begivenhed med 20+ NoteProperty-objekter inde,

    hver tager tid at skabe.

  • Export-CSV skal have adgang til hver NoteProperty og derefter bruge den langsomme PS-rørledning

  • | pipelining er langsommere end flow kontrol sætninger som foreach (ikke cmdlet), mens.



Lad os blive beskidte og gøre alt manuelt ved hjælp af .NET 3.5 + og 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()
}


Nu påberåber det parallelt med RunSpaces til yderligere forbedringer:


$outputDir = 'C:UsersAdministratorDesktopfolder4768'

Get-Content C:usersAdministratorDesktopfullfiles.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
    }

Andre referencer 1


Du kan måske se på at oprette Runspaces og RunspacePools. De er lidt vanskelige at oprette, men de er meget seje og meget effektive i den måde, de arbejder på. Puljerne giver dig mulighed for at indstille en gasspjæld. RunspaceFactory lægger et job i 1 af x slots, så en gang en afslutter den kaster en anden i den slot.
En af de store fordele ved dette er den overhead - eller mangel på overhead, snarere. Runspaces kræver ikke en anden forekomst af PowerShell, mens Jobs gør. [5]


Så hvis du har en RunspacePool med en bredde på 50 og 1000 job at behandle, vil 50 af disse job køre på et og samme tidspunkt. Super sejt.


Tjek dette eksempel fra mjolinor.
Indrømmet, det er et ret komplekst eksempel. Han har gennemgået en masse problemer med at få adgang til de forskellige datastrømme. Det gør det muligt at overvåge processerne i puljen. [6]