Arbelac
Arbelac

Reputation: 1904

Optimize PowerShell Performance and Memory Consumption

The PowerShell script below queries the Security event log on one or more servers for events with id 4663. When trying to retrieve all audit events for event id 4663 with the following code the computer throws the following exception: how can we to optimize this PowerShell? So I just want to fetch security event log based on specific AD Users instead of all of users. Otherwise I want to retrieve what I need.

enter image description here enter image description here

$server = "HOSTNAME"
$out = New-Object System.Text.StringBuilder
$out.AppendLine("ServerName,EventID,TimeCreated,UserName,File_or_Folder,AccessMask")
$ns = @{e = "http://schemas.microsoft.com/win/2004/08/events/event"}
foreach ($svr in $server)
    {    $evts = Get-WinEvent -computer $svr -FilterHashtable @{logname="security";id="4663"} -oldest

    foreach($evt in $evts)
        {
        $xml = [xml]$evt.ToXml()

        $SubjectUserName = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[@Name='SubjectUserName']/text()" | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty Value

        $ObjectName = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[@Name='ObjectName']/text()" | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty Value

        $AccessMask = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[@Name='AccessMask']/text()" | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty Value

        $out.AppendLine("$($svr),$($evt.id),$($evt.TimeCreated),$SubjectUserName,$ObjectName,$AccessMask")

        Write-Host $svr
        Write-Host $evt.id,$evt.TimeCreated,$SubjectUserName,$ObjectName,$AccessMask

        }
    }
$out.ToString() | out-file -filepath C:\TEMP\4663Events.csv

Upvotes: 1

Views: 1727

Answers (1)

vonPryz
vonPryz

Reputation: 24071

Refactor the code so that the stringbuilder contents are flushed every now and then. This keeps its size more manageable. Like so,

$out = New-Object System.Text.StringBuilder
$out.AppendLine("ServerName,EventID,TimeCreated,UserName,File_or_Folder,AccessMask")
foreach($evt in $evts) {
    $xml = [xml]$evt.ToXml()
    ...
    $out.AppendLine("$($svr),$($evt.id),$($evt.TimeCreated),$SubjectUserName,$ObjectName,$AccessMask")
    # If the stringbuffer is large enough, flush its contents to disk
    # and start filling empty buffer again
    if($out.length -ge 100MB) {
        $out.ToString() | out-file -filepath C:\TEMP\4663Events.csv -append
        $out.Clear()
    }
}
# Remember to flush the buffer so that stuff that wasn't flushed in the
# foreach loop is saved as well
$out.ToString() | out-file -filepath C:\TEMP\4663Events.csv -append

Edit:

As the error is coming from Out-LineOutput, which is an internal cmdlet, it might be about the shell memory settings. You could try increasing the maximum memory per shell to, say 2 GB. Like so,

Set-Item .\MaxMemoryPerShellMB 2048

The MS Scripting Guys blog has a detailed article about configuring the limits

Upvotes: 2

Related Questions