Select Some Strings From Event message - powershell

I am trying to pull all username,time created and public ip address from
Microsoft-Windows-TerminalServices-Gateway/Operational event.
I get all the events from messages with below command but I need only username,and ip
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}
Trying with this No luck
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}| select timecreated -expand Message [regex]::Match($event, 'user:\s*(.*)\s*').Groups[1].Value

Ok, I can see what you're trying with the Select command, and while it sounds good, that doesn't quite work. What you could do instead is pass it through a Where statement matching the string you need, and capturing the relevant data in a RegEx match, then using Select to add those fields onto the object.
So a Where match that would work for you should be:
| Where{$_.Message -match '"(.+?\\.+?)".+"(\d+\.\d+\.\d+\.\d+)"'}
Then you pipe to Select and build the User and IPAddress properties on the fly like this:
| Select TimeCreated,#{l='User';e={$Matches[1]}},#{l='IPAddress';e={$Matches[2]}}
Then you put it all together and you get:
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303} | Where{$_.Message -match '"(.+?\\.+?)".+"(\d+\.\d+\.\d+\.\d+)"'} | Select TimeCreated,#{l='User';e={$Matches[1]}},#{l='IPAddress';e={$Matches[2]}}
Using that with some randomized values I was able to output:
TimeCreated User IPAddress
----------- ---- ---------
9/14/2016 9:47:29 AM DOMAIN\dhxrjqb 216.229.149.87
9/14/2016 9:47:29 AM DOMAIN\fkoilrh 236.65.23.77
9/14/2016 9:47:29 AM DOMAIN\mvibope 20.7.45.231
Ok, the solution to the issue that you noted in your comment is a minor tweak to the RegEx match to make one portion not greedy. This would do that:
| Where{$_.Message -match '"(.+?\\.+?)".+?"(\d+\.\d+\.\d+\.\d+)"'}
and all together again...
get-winevent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303} | Where{$_.Message -match '"(.+?\\.+?)".+?"(\d+\.\d+\.\d+\.\d+)"'} | Select TimeCreated,#{l='User';e={$Matches[1]}},#{l='IPAddress';e={$Matches[2]}}

I think there's a better approach using the ToXml() Method on the EventLogRecord Object:
First Catch the Events:
$Events = Get-WinEvent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}
Then, Take for example the first item in the array and convert it to XML:
[xml]$Event = $Events[0].ToXml()
Now you can see all the Information you need:
$Event.Event.UserData.EventInfo
Update: Set it for you, this should do the work:
$Events = Get-WinEvent -FilterHashtable #{Logname = "Microsoft-Windows-TerminalServices-Gateway/Operational" ; ID = 300,302,303}
$ArrayList = New-Object System.Collections.ArrayList
Foreach ($Event in $Events)
{
[xml]$Xml = $Event.ToXml()
$Row = "" | Select Username,TimeCreated,IPAddress
$Row.Username = $Xml.Event.UserData.EventInfo.Username
$Row.TimeCreated = $Event.TimeCreated.ToString()
$Row.IPAddress = $Xml.Event.UserData.EventInfo.IpAddress
[void]$ArrayList.Add($Row)
}
$ArrayList

Related

(PowerShell) How do I filter usernames with Get-EventLog

I'm working on a Powershell script to get all users who have logged in/out of a server in the past 7 days, where their name is not like "*-organization". The below works, but no matter what I try I'm not able to filter names
$logs = get-eventlog system -ComputerName $env:computername -source Microsoft-Windows-Winlogon -After (Get-Date).AddDays(-7)
$res = #()
ForEach ($log in $logs)
{
if($log.instanceid -eq 7001){
$type = "Logon"
}
Elseif ($log.instanceid -eq 7002){
$type = "Logoff"
}
Else { Continue }
$res += New-Object PSObject -Property #{Time = $log.TimeWritten; "Event" = $type; User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])}};
$res
I've tried adding this line in various places and ways, but no matter what I can't get it to filter. It either fails and tells me my operator must have a property and value, or it runs fine and ignores any username filtering.
| Where-Object $_.User -notlike "*-organization"
Is it even possible to filter the login username with this method? If so, what am I doing wrong? If it's not possible, is there another way I can get what I need?
There would have to be a property named 'user' for that to work. Get-eventlog is actually obsolete now, and replaced by get-winevent. Unfortunately, you have to get into the xml to filter by usersid. I've included a time filter.
$a = get-winevent #{logname='system';
providername='Microsoft-Windows-Winlogon'} -MaxEvents 1
$e = $a.ToXml() -as 'xml'
$e.event.EventData
Data
----
{TSId, UserSid}
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
data='S-2-6-31-1528843147-473324174-2919417754-2001';starttime=(Get-Date).AddDays(-7);
id=7001,7002}
In powershell 7 you can refer to the eventdata named data fields directly:
get-winevent #{logname='system';providername='Microsoft-Windows-Winlogon';
usersid='S-2-6-31-1528843147-473324174-2919417754-2001'}
The get-winevent docs say you can use "userid" in the filterhashtable, but I can't get that to work.
EDIT: Actually this works. But without limiting it too much, at least for me.
get-winevent #{logname='system';userid='js2010'}
get-winevent #{providername='Microsoft-Windows-Winlogon';userid='js2010'}
You can do this with the -FilterXPath parameter like below:
$filter = "(*[System/EventID=7001] or *[System/EventID=7002]) and *[System/Provider[#Name='Microsoft-Windows-Winlogon']]"
$result = Get-WinEvent -LogName System -FilterXPath $filter | ForEach-Object {
# convert the event to XML and grab the Event node
$eventXml = ([xml]$_.ToXml()).Event
$eventData = $eventXml.EventData.Data
$userSID = ($eventData | Where-Object { $_.Name -eq 'UserSid' }).'#text'
$userName = [System.Security.Principal.SecurityIdentifier]::new($userSID).Translate([System.Security.Principal.NTAccount])
# you can add username filtering here if you like.
# remember the $userName is in formal DOMAIN\LOGONNAME
# if ($username -notlike "*-organization") {
# output the properties you need
[PSCustomObject]#{
Time = [DateTime]$eventXml.System.TimeCreated.SystemTime
Event = if ($eventXml.System.EventID -eq 7001) { 'LogOn' } else { 'LogOff' }
UserName = $userName
UserSID = $userSID
Computer = $eventXml.System.Computer
}
# }
}
# output on screen
$result
# output to CSV file
$result | Export-Csv -Path 'X:\TheOutputFile.csv' -NoTypeInformation
Note, I have commented out the username filtering in the code. It is just there to give you an idea of where to put it. Of course, you can also filter the $result afterwards:
$result | Where-Object { $_.UserName -notlike "*-organization" }
Adding to #js2010's helpful answer, and with the assumption you're using PowerShell 5.1. I usually identify the property array index and use Select-Object to create a custom property as needed.
$WinEvents =
get-winevent #{logname='system'; providername='Microsoft-Windows-Winlogon'} |
Select-Object #{Name = 'Time'; Expression = {$_.TimeCreated}},
#{Name = 'Event'; Expression = { If($_.ID -eq 7001){'Logon'} ElseIf($_.ID -eq 7002){ 'Logoff' } } },
#{Name = 'User'; Expression = { [System.Security.Principal.SecurityIdentifier]::new( $_.Properties[1].Value ).Translate([System.Security.Principal.NTAccount]) } }
In your case this should add a property called User with a value like DomainName\UserName to the objects. I also added expressions to derive the other properties you were adding to your custom objects. Select-Object emits custom objects as well so this should give the result you're looking for.
Let me know if this helps.
Update
Respectfully, the other 2 answers make the assumption that you are looking for logon/off events for a specific user. That's not how I read the question; in particular:
"get all users who have logged in/out of a server"
While PowerShell 7+ does let you directly cite UserID in the FilterHashtable, it's not very useful here because we're not seeking events for a specific user. Furthermore, it seems unhelpful for the ultimate output as by default it echoes as a SID. It would still need to be translated, not only for display but for further filtering. I'm also not positive that UserID will always be the same as Properties[1], there's certainly some variance when looking at other event IDs.
The XML work is very cool, but I don't think it's called for here.
There were some issues with my answer as well. I overlooked filtering the event IDs & dates up front. I also realized we don't need to instantiate [System.Security.Principal.SecurityIdentifier] class because the property is already typed as such. Along with some readability improvements I corrected those issues below.
# Should be the 1st line!
using NameSpace System.Security.Principal
$ResolveEventType = #{ 7001 = 'Logon'; 7002 = 'Logoff' }
$FilterHashTable =
#{
LogName = 'system'
ProviderName = 'Microsoft-Windows-Winlogon'
ID = 7001,7002
StartTime = (Get-Date).AddDays(-7)
}
[Array]$WinEvents =
Get-WinEvent -FilterHashtable $FilterHashTable |
Select-Object #{ Name = 'Time'; Expression = { $_.TimeCreated } },
#{ Name = 'Event'; Expression = { $ResolveEventType[ $_.ID ] } },
#{ Name = 'User'; Expression = { $_.Properties[1].Value.Translate( [NTAccount] ) } }
$WinEvents |
Where-Object{ $_.UserName -notlike "*-organization" } |
Format-Table -AutoSize
This tested good in PowerShell 5.1 & 7.0. I added Format-Table to display the output, but you can just change that out for an Export-Csv command as needed
Note: The last 2 pipelines can be combined, but I thought this was a
little more readable.
Let me know if this helps.

Advanced Event Viewer filters

Exist the possibility to extract some information from Event viewer about event id description via powershell?
For example i want to see if I have on domain controller this event id 4776 with description "Authentication Package: WDigest".
You can use Get-WinEvent's -FilterXPath parameter to very granularly filter against the raw event xml.
In your case we could find all WDigest auth events with:
Get-WinEvent -FilterXPath "*[System[EventID=4776] and EventData[Data[#Name='PackageName']='WDigest']]" -LogName "Security"
It would be easier with the logname or providername, but it's possible to search all the logs, unlike in the event viewer. You can't just say '*' for the logname, because of a 256 logname query limit in the windows api:
get-winevent -listlog * |
foreach {
get-winevent #{ logname = $_.logname; id = 4776 } -ea 0 } |
where message -match 'Authentication Package: WDigest'
In powershell 6 or 7 you can filter on named eventdata:
get-winevent #{ Logname = 'Security'; ID = 4776; PackageName = 'WDigest' }
In powershell 5 maybe this. Data can't have wildcards.
get-winevent #{ Logname = 'Security'; ID = 4776; Data = 'WDigest' }

Remote Desktop Services login history for specific user

I found a script here: https://serverfault.com/questions/479048/remote-desktop-services-login-history
Here is the script:
Get-Eventlog -LogName Security | where {$_.EventId -eq "4624"} | select-object #{Name="User"
;Expression={$_.ReplacementStrings[5]}} | sort-object User -unique |ogv
The goal is to search for a specific user and see when was the last time that he have login to the terminal server, and with that script, i'am unable to make it to show the date too, only the user name, I've tried to add some property after running get-member, but didn't got any success
thank you for your help
You can use the Get-WinEvent cmdlet for this like below:
$user = 'The SamAccountName of the user you want to track'
Get-WinEvent -FilterHashtable #{LogName='Security';ID=4624} -MaxEvents 100 |
Where-Object {$_.Properties[5].Value -eq $user } |
Select-Object -Property #{Name = 'UserName'; Expression = { $_.Properties[5].Value }},
#{Name = 'LogonTime'; Expression = { $_.TimeCreated }},
MachineName |
Out-GridView
# $_.Properties[5].Value --> TargetUserName
The -MaxEvents 100 is just an example. Change that value or remove the parameter alltogether if you need to
To retrieve only 3events, use the -MaxEvents parameter with value 3.
You can also select the (last) 3 events afterwards if that is what you want by appending -Last 3 to the Select-Object command.
To see what the Properties array contains for this event ID, you can do
$props = (Get-WinEvent -MaxEvents 1 -FilterHashtable #{LogName='Security';ID=4624}).Properties
for ($i = 0; $i -lt $props.Count; $i++) {
"Properties[$i].Value --> {0}" -f $props[$i].Value
}
Comparing this to what you can read in the XML-view of eventvwr.exe:
SubjectUserSid = 0
SubjectUserName = 1
SubjectDomainName = 2
SubjectLogonId = 3
TargetUserSid = 4
TargetUserName = 5
TargetDomainName = 6
TargetLogonId = 7
LogonType = 8
LogonProcessName = 9
AuthenticationPackageName = 10
WorkstationName = 11
LogonGuid = 12
TransmittedServices = 13
LmPackageName = 14
KeyLength = 15
ProcessId = 16
ProcessName = 17
IpAddress = 18
IpPort = 19
These values differ when asking for other events and are only valid for LogName='Security';ID=4624
Try this with help from https://community.spiceworks.com/topic/2067489-powershell-script-to-get-rdp-session-history
Get-WinEvent -FilterHashTable #{LogName='Security';ID=4624} | Where-Object {$_.Properties[5].Value -eq 'UserName'} | Select-Object #{n='User';e={$_.Properties[5].Value}},#{n='Logon time';e={$_.Timecreated}}
Replace 'UserName' with your desired user.

get-winevent: cannot filter specific field FailureReason %%2313

I'm poking around with get-winevent for the event 4625 because i need the failure-reason, which is in a xml field of the event.
the first thing i tried is to print out all fields hoping, the failure reason is available somewhere in the data-path of the xml, with no success.
i did not found a way to query the statuscode %%2313 to something with powershell to get the readable text.
the last thing i did is filtering the specific event and write the statusmessage myself in a custom field of my export file.
I used this code to accomplish that:
Get-WinEvent -FilterHashtable #{Path="c:\temp\test.evtx";} -ErrorAction SilentlyContinue |
Where-Object {($_.id -eq "4625" -and $_.properties[9].value -in "%% 2313")}|ForEach-Object{
$SelectorStrings = [string[]]#(
'Event/EventData/Data[#Name="TargetUserName"]',
'Event/EventData/Data[#Name="TargetDomainName"]',
'Event/EventData/Data[#Name="WorkstationName"]',
'Event/EventData/Data[#Name="IpAddress"]',
'Event/EventData/Data[#Name="IpPort"]',
'Event/EventData/Data[#Name="LogonType"]',
'Event/EventData/Data[#Name="FailureReason"]',
'Event/EventData/Data[#Name="Remark"]'
)
$PropertySelector = [System.Diagnostics.Eventing.Reader.EventLogPropertySelector]::new($SelectorStrings)
$TargetUserName,$TargetDomainName,$WorkstationName,$IpAddress,$IpPort,$LogonType,$FailureReason,$Remark = $_.GetPropertyValues($PropertySelector)
$Remark ="Unbekannter Benutzername oder ungültiges Kennwort."
#Create the PSCustomObject from the given Fieldnames
[PSCustomObject]#{
TimeCreated = $_.TimeCreated
Id = $_.Id
UserName = $TargetUserName
Domain = $TargetDomainName
WorkstationName = $WorkstationName
IPAddress = $IpAddress
Port = $IpPort
LogonType = $LogonType
Message = ($_.Message).split(".")[0]
Remark = $Remark
FailureReason =$FailureReason
}
}|Export-Csv -NoTypeInformation -Force -Encoding UTF8 -Path 'c:\temp\failurereason.csv'
but: it seams i cannot filter for %%2313 as property value. no idea why. Even with quotation marks or something, dont get any event out.

Powershell, -filterhashtable, and operators

I'm filtering event log entries using the "Get-Winevent" cmdlet. I want to get events whose levels are less than 4 (or where LevelName isn't "Informational").
I use the -filterhashtable flag to filter the events. But is there a way to do comparisons with filterhashtable? Or just put a "not"? Or does filterhashtable only accept "=" as an operator?
These two snippets work and get the same results:
where-object
$events = Get-WinEvent -computer ServerName -LogName System | Where-Object {$_.level -lt 4}
-filterhashtable
$events = Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level = 1}
$events += Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level = 2}
$events += Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level = 3}
The second snippet runs much faster than the first snippet (2 minutes versus 16 seconds in one case). As I understand it, "where-object" has to wait until "Get-WinEvent" has gotten every event object (possibly thousands). Adding "-filterhashtable" causes the target system's event log to filter before it gives the event object ot Get-WinEvent, which is much faster.
Can I combine the statements? These snippets don't work:
$events = Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level < 4}
$events = Get-WinEvent -computer ServerName -FilterHashTable #{LogName = 'System'; Level != 2}
The "Level" properties is type "int[32]" so a comparison operator should work. In fact, it does work with "where-object". But it doesn't work with the "-filterhashtable" flag. Is there no way to do that sort of comparison? Is "=" the only operator -filterhashtable accepts?
No dice on operators like that. The FilterXPath parameter supports that. However the help on the FilterHashtable parameter indicates it takes an array of int, so it would accept:
... -FilterHashtable #{LogName='System';Level=0,1,3}
No, you cant. A hashtable is a collection of key = value pairs, so it won't allow relational operators.
Btw, in Powershell < is -lt and > is -gt.