0

I'm cannibalising the script made available here to try and parse the security event log for certain file audit events. What I am trying to do is restrict the output to events that match a specific file name and access mask.

The event parser uses System.Text.StringBuilder to construct the results object ($evt), and I want to filter this to get the events I actually want.

Here's some of the raw output:

23/09/2015 10:50:23 AM userid F:\dir1 0x1
23/09/2015 10:50:23 AM userid F:\dir1\dir2 0x1
23/09/2015 10:50:23 AM userid F:\dir1\dir2\doc.docx 0x20000

The last line in the sample is the kind I'm trying to trap.

I'm creating each line as key/data pairs per the following

$out.AppendLine("TimeCreated = $($evt.TimeCreated),Username = $SubjectUserName,File = $ObjectName,AccessMask = $AccessMask")

Then I found that ConvertFrom-StringData isn't happy with the backslashes in the file paths, so fixed that to create the hashtable:

$output = $out.ToString() -replace '\\', '\\'
$hash = convertfrom-stringdata -stringdata $output

But now I'm getting the following error:

convertfrom-stringdata : Data item 'TimeCreated' in line 'TimeCreated =
09/23/2015 10:50:23,Username = userid,File = F:\\dir1,AccessMask = 0x1'
is already defined.

I suspect I need to go back to first principles to filter event data with a known eventID and specific contents rather than mucking around with strings and hashtables (maybe a custom PSObject?), but can anyone illuminate what this error is about? Or a better way?

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
LeeM
  • 1,118
  • 8
  • 18

1 Answers1

0

Filter the results before creating the output string:

$filename = 'F:\dir1\dir2\doc.docx'
$mask     = '0x20000'

...

if ($ObjectName -eq $filename -and $AccessMask -eq $mask) {
  $out.AppendLine("$($svr),$($evt.id),$($evt.TimeCreated),$SubjectUserName,$ObjectName,$AccessMask")
}

On a more general note, I'd say a more PoSh approach would be to create the data as custom objects and leave the CSV creation to Export-Csv:

foreach ($svr in $server) {
  Get-WinEvent -Computer $svr -FilterHashtable @{logname="security";id="4663"} -Oldest | ForEach-Object {
    $xml = [xml]$_.ToXml()

    $props = [ordered]@{
      'ServerName'     = $svr
      'EventID'        = $_.Id
      'TimeCreated'    = $_.TimeCreated
      'UserName'       = Select-Xml -Xml $xml -Namespace $ns -XPath "//e:Data[@Name='SubjectUserName']/text()" | Select-Object -ExpandProperty Node | Select-Object -ExpandProperty Value
      'File_or_Folder' = 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
    }

    New-Object -Type PSCustomObject -Property $props
  } | Where-Object {
    $_.'File_or_Folder' -eq $filename -and
    $_.AccessMask -eq $mask
  } | Export-Csv 'C:\Temp\4663Events.csv' -NoType -Append
}

I haven't compared the two approaches performance-wise, though.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • That looks much better - thanks so much. I'm not too concerned about how long it takes to execute. – LeeM Sep 24 '15 at 00:47