So lets take a deeper look into Window Event Messages.
Each message has a template. You can look at the templates like
(Get-WinEvent -ListProvider * -ErrorAction Ignore).Events |
select Id, Version, Template |
Format-List
We can dig down to find the event we are looking for also like
(Get-WinEvent -ListProvider * -ErrorAction Ignore).Events |
Where-Object {$_.Id -eq 4663} |
select Id, Version, Template |
Format-List
We can see there are 2 versions of the template used in windows. We can also see what the proper names are also.
Template : <template xmlns="http://schemas.microsoft.com/win/2004/08/events">
<data name="SubjectUserSid" inType="win:SID" outType="xs:string"/>
<data name="SubjectUserName" inType="win:UnicodeString" outType="xs:string"/>
<data name="SubjectDomainName" inType="win:UnicodeString" outType="xs:string"/>
<data name="SubjectLogonId" inType="win:HexInt64" outType="win:HexInt64"/>
<data name="ObjectServer" inType="win:UnicodeString" outType="xs:string"/>
<data name="ObjectType" inType="win:UnicodeString" outType="xs:string"/>
<data name="ObjectName" inType="win:UnicodeString" outType="xs:string"/>
<data name="HandleId" inType="win:Pointer" outType="win:HexInt64"/>
<data name="AccessList" inType="win:UnicodeString" outType="xs:string"/>
<data name="AccessMask" inType="win:HexInt32" outType="win:HexInt32"/>
<data name="ProcessId" inType="win:Pointer" outType="win:HexInt64"/>
<data name="ProcessName" inType="win:UnicodeString" outType="xs:string"/>
<data name="ResourceAttributes" inType="win:UnicodeString" outType="xs:string"/>
</template>
In the post we want to get access mask. We can see in the template
<data name="AccessMask" inType="win:HexInt32" outType="win:HexInt32"/>
So we are going to get all events that meet the ID we want which is 4663 and limit the output to 10
Get-WinEvent -logname security -FilterXPath "*[System[EventID=4663]]" -MaxEvents 10
We will turn that output into XML and parse down to we get to those settings then create a PSObject to store all those settings. Then we will add each PSObject to a ArrayList.
$ArrayList = New-Object System.Collections.ArrayList
Get-WinEvent -logname security -FilterXPath "*[System[EventID=4663]]" -MaxEvents 10 | %{
$XML = [xml]$_.toXml()
$PsObject = New-Object psobject
$XML.Event.EventData.Data | %{
$PsObject | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_."#text"
}
$ArrayList.add($PsObject) | out-null
}
$ArrayList | Select AccessMask
We select only AccessMask on the arraylist and we are good.
On a End Note I have written a function for this.
function Parse-WindowsEvents(){
param(
[Parameter(Position=1, ValueFromPipeline)]
[object[]]$Events
)
process{
$ArrayList = New-Object System.Collections.ArrayList
$Events | %{
$EventObj = $_
$EventObjFullName = $_.GetType().FullName
if($EventObjFullName -like "System.Diagnostics.EventLogEntry"){
$EventObj = Get-WinEvent -LogName security -FilterXPath "*[System[EventRecordID=$($_.get_Index())]]"
}elseif($EventObjFullName -like "System.Diagnostics.Eventing.Reader.EventLogRecord"){
}else{
throw "Not An Event System.Diagnostics.Eventing.Reader.EventLogRecord or System.Diagnostics.EventLogEntry"
}
$PsObject = New-Object psobject
$EventObj.psobject.properties | %{
$PsObject | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.Value
}
$XML = [xml]$EventObj.toXml()
$PsObject2 = New-Object psobject
$XML.Event.EventData.Data | %{
$PsObject2 | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_."#text"
}
$PsObject | Add-Member -MemberType NoteProperty -Name ParsedMessage -Value $PsObject2
$ArrayList.add($PsObject) | out-null
}
return $ArrayList
}
}
Example Usages
Get-EventLog -LogName Security | select -first 3 | Parse-WindowsEvents | select id, recordid -ExpandProperty parsedmessage | fl
or
get-winevent -logName security | parse-winevents
The function will add a new property to the object called ParsedMessage