My short term goal is to gather event IDs 40 and 42 with provider Microsoft-Windows-TerminalServices-LocalSessionManager
from the log named Microsoft-Windows-TerminalServices-**LocalSessionManager/Operational
then sort them based on the Session/SessionID. While I could try and parse the message property, I'm trying to use the EventLogRecord's GetPropertyValues method which takes a System.Diagnostics.Eventing.Reader.EventLogPropertySelector
parameter. Also, the session ID shows up first labeled "Session" for event 40's message and shows up second labeled "SessionID" for event 41's message.
Name | MemberType | Definition |
---|---|---|
GetPropertyValues | Method | System.Collections.Generic.IList[System.Object] |
I've see that in 2018 Peter-Core and Mathias R. Jessen have both given answer on how to do this. Following their examples, I looked at the event provider's event template.
(Get-WinEvent -ListProvider Microsoft-Windows-TerminalServices-LocalSessionManager).Events|Where-object{@(40,42) -contains $_.ID}
Id : 40
Version : 0
LogLink : System.Diagnostics.Eventing.Reader.EventLogLink
Level : System.Diagnostics.Eventing.Reader.EventLevel
Opcode : System.Diagnostics.Eventing.Reader.EventOpcode
Task : System.Diagnostics.Eventing.Reader.EventTask
Keywords : {}
Template : <template xmlns="http://schemas.microsoft.com/win/2004/08/events">
<data name="Session" inType="win:UInt32" outType="xs:unsignedInt"/>
<data name="Reason" inType="win:UInt32" outType="xs:unsignedInt"/>
</template>
Description : Session %1 has been disconnected, reason code %2
Id : 42
Version : 0
LogLink : System.Diagnostics.Eventing.Reader.EventLogLink
Level : System.Diagnostics.Eventing.Reader.EventLevel
Opcode : System.Diagnostics.Eventing.Reader.EventOpcode
Task : System.Diagnostics.Eventing.Reader.EventTask
Keywords : {}
Template : <template xmlns="http://schemas.microsoft.com/win/2004/08/events">
<data name="User" inType="win:UnicodeString" outType="xs:string"/>
<data name="SessionID" inType="win:UInt32" outType="xs:unsignedInt"/>
</template>
Description : End session arbitration:
User: %1
Session ID: %2
As a test, I created the following to try and get the Session ID from the event ID 42:
$strComputerName='PMV-SC01'
<# For those wanting to build xmlFilters try something like the following as it allow you to create them more dynamically
$xmlFilterBase=[xml]'<QueryList><Query Id="0"></Query></QueryList>'
## in a loop ##
$xmlSelect=$xmlFilter.CreateElement("Select")
$xmlSelect.SetAttribute("Path","$Logname")
$xmlSelect.set_InnerText("*[System[$($ProviderName)TimeCreated[@SystemTime>='$StartTime' and @SystemTime<='$EndTime']]]")
$xmlFilter.QueryList.Query.AppendChild($xmlSelect)|Out-Null
$xmlSelect=$Null
## end loop ##
#>
#I used the filter below to simplify the testing.
$xmlFilter=@"
<QueryList><Query Id="0" Path="Microsoft-Windows-TerminalServices-LocalSessionManager/Operational"><Select Path="Microsoft-Windows-TerminalServices-LocalSessionManager/Operational">*[System[TimeCreated[@SystemTime>='2021-06-28T22:00:00.000Z' and @SystemTime<='2021-06-28T23:00:00.000Z']]]</Select></Query></QueryList>
"@
$WinEvents=Get-WinEvent -ComputerName $strComputerName -FilterXml $xmlFilter -Oldest|Where-object{$_.Id -eq 42}
$WinEvents.count
# output is 10
$SelectorStrings= [String[]]@(
'Event/EventData/Data[@name="SessionID"]'
)
$PropertySelector= [System.Diagnostics.Eventing.Reader.EventLogPropertySelector]::new($SelectorStrings)
$WinEvents|ForEach-Object{$TheSessionIds=@();$TheSessionIds+=$_.GetPropertyValues($PropertySelector)}
$TheSessionIds.count
#output is 1
# All of the follow also return nothing:
$WinEvents[1].GetPropertyValues($PropertySelector)
$WinEvents.GetPropertyValues($PropertySelector)
$1WinEvent=$WinEvents[0]
$1WinEvent.GetPropertyValues($PropertySelector)
There is no error message. The GetPropertyValues() method just returns nothing. PowerShell Version: 5.1.14393.2457
Windows | Edition | Version | OS Build |
---|---|---|---|
Windows | Server 2016 Standard | 1607 | 14393.2457 |
My long term goal is generating something like following output. The request on this post is help getting values back from the GetPropertyValues method. I'd also like to use this method in other projects.
SessionID | User | TimeCreated | ID | Code |
---|---|---|---|---|
12 | MYDOMAIN\User.Name | 2021-06-28 07:35:54.4321 | 42 | |
12 | 2021-06-28 15:35:54.4321 | 40 | 0 | |
13 | MYDOMAIN\Other.User | 2021-06-28 11:12:44.1234 | 42 | |
13 | 2021-06-28 13:01:12.5678 | 40 | 0 |
If you examine a sample XML record you can see the path to the node is not under /EventData/Data. It took some fiddling but this worked perfect for me.
$xmlFilter = @"
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TerminalServices-LocalSessionManager/Operational">
<Select Path="Microsoft-Windows-TerminalServices-LocalSessionManager/Operational">*[System[(EventID=40 or EventID=42)]]</Select>
</Query>
</QueryList>
"@
$SelectorStrings= [String[]]@(
'Event/UserData/EventXML/User',
'Event/UserData/EventXML/Session',
'Event/UserData/EventXML/Reason'
)
Get-WinEvent -FilterXml $xmlFilter | ForEach-Object {
switch ($_.id){
40 {$SelectorStrings[1] = 'Event/UserData/EventXML/Session'}
42 {$SelectorStrings[1] = 'Event/UserData/EventXML/SessionID'}
}
$PropertySelector= [System.Diagnostics.Eventing.Reader.EventLogPropertySelector]::new($SelectorStrings)
$user,$sessionID,$reason = $_.GetPropertyValues($PropertySelector)
[PSCustomObject]@{
SessionID = $sessionID
User = $user
TimeCreated = $_.TimeCreated
ID = $_.ID
Code = $reason
}
}