Showing posts with label get-winevent. Show all posts
Showing posts with label get-winevent. Show all posts

28 January, 2017

Parse user name from events - OS

Have you ever needed to parse fields or text from Windows Event Log? Even if the answers is no, read on. You never know when someone stops you on the road and holds a gun to your head and shouts until you parse all the fields from those 100 000 events.


My example was just about parsing user names from events in the Application log in a very friendly situation - no guns and shouting involved...
There were 300 000+ of these events in the last 24 hours and I had to find out from them the list of users with connection to the app and a connection count for each user in the same time frame.


This is how the events I'm parsing look like:

Event with User name to parse and count


First attempt

I used Get-WinEvent and filtered in its hash based switch as much as possible to speed up the query for events. Something like:
$timelimit=(Get-Date).addhours(-24);
Get-WinEvent -ea SilentlyContinue -FilterHashtable @{LogName="Application"; StartTime=$timelimit; id=998}


The events will tell me the SID (Security IDentifier) of the user who generated the event, to look up the user name I used the SecurityIdentifier and then the NTAccount classes of System.Security.Principal:
Select @{e={(New-Object System.Security.Principal.SecurityIdentifier($_.userID)).Translate([System.Security.Principal.NTAccount])}; l="User";}


The trick to make this a one-liner was to generate the user name on the fly when creating a custom object with 'Select' in the command


Full command and the measure of run time:
measure-command {$timelimit=(Get-Date).addhours(-24); $tmp=Get-WinEvent -ea SilentlyContinue -FilterHashtable @{LogName="Application"; StartTime=$timelimit; id=998} | Select @{e={(New-Object System.Security.Principal.SecurityIdentifier($_.userID)).Translate([System.Security.Principal.NTAccount])}; l="User";} | group user}


Days              : 0
Hours             : 0
Minutes           : 41
Seconds           : 0
Milliseconds      : 11

Second attempt - with regex parsing


Well, 41 minutes doesn't look like a lot of time when you are spending with your loved ones, but it's way above the limit of the patience of an average IT guy... Remember the first part of this article? I was lucky enough with this application that it logged the user name into the message of the event (with some other random text as well).


So let's try something different:
measure-command {$timelimit=(Get-Date).addhours(-24); $tmp=Get-WinEvent -ea SilentlyContinue -FilterHashtable @{LogName="Application"; StartTime=$timelimit; id=998} | Select @{e={$matches=$null;$a=($_.message -match "UserName: (?<users>[^`n]+)"); $matches.user}; l="User";} | group user}


Days              : 0
Hours             : 0
Minutes           : 8
Seconds           : 13
Milliseconds      : 54


Very nice, 80% reduction on run time. What's different on this run?
The reason why this is quicker because the command does not read into Active Directory to look up a SID and translate it to user name, instead, it just parses the user name from the message field of the event with a regex pattern:
$matches=$null;$a=($_.message -match "UserName: (?<users>[^`n]+)"); $matches.user



Hope this helps saving a bit of time for some of you and can spend this away from the computer thinking about the next powershell trick!


t

15 March, 2013

Enumerate eventlog: NETLOGON errors of broken secure channel - AD

If you have a big Active Directory, you will always have noise in the eventlog of your domain controllers which is not what you want because you might miss the wood... you know, you can't see the wood from the trees.
Sometimes the noisiest one is the NETLOGON service because whenever a machine which either forgot its password or has a broken secure channel or doesn't have an account in AD tries to connect to a DC, Netlogon service throws and error to the System log. Don't ask me why it's an error, in my view it should be a warning (tops) as it's not really an error of NETLOGON. Moreover, if I was MSFT, I would have made it an optional event turned on/off via registry, similar to the NTDS diagnostics events under HKLM\SYSTEM\CurrentControlSet\services\NTDS\Diagnostics.

Anyway, let's not dwell on it but try to do something about it. If you are a conscientious AD guy (and why wouldn't you be, we all are conscientious when it's about work ;) ) you want to make things right. First step: let's identify the machines which have broken secure channel. It shouldn't be difficult, the machine name is part of the event message. However, I have 100+ domain controllers, 30+ sites, reading through the eventlog on a regular basis is not an option. Need a script!

The script which you'll see at the bottom of the article is capable of:
  • Enumerating the netlogon events from a DC and parse the error message and the client name from the event description
  • Can work against 1 DC or a list of DCs in a specified site

Some interesting facts about the script. It uses Get-WinEvent command. If you use it remotely, it can be quite slow, e.g. let's list all events with EventID 5805 from the System Event log:
Get-WinEvent -ea SilentlyContinue -ComputerName c3poDC -LogName System | where{$_.id -eq 5805}

It takes a bit more than 30 seconds:

 
















Obviously, the biggest issue is that it takes all events and then filters to the eventid afterwards. Let's try a trick, hash table. It's in the help of the command that it takes filters in hash table format. Excellent, let's try this then:
Get-WinEvent -ea SilentlyContinue -ComputerName c3poDC -FilterHashtable @{LogName = "System"; id=5805}

Hmmm... not bad, 4 seconds, now we are talking.
















We can just dress up the script a bit:
  • take an integer which determines how many days we want to go back in the log (makes the query even quicker):
    $after = (Get-Date).adddays(-$lastday)
    Get-WinEvent -ea SilentlyContinue -ComputerName $srv -FilterHashtable @{LogName = "System"; StartTime = $after; id=5805}
  • take DC name which we want to query
  • take site name, enumerate the DCs in the site, and then run through them:
    $dclist = Get-ADDomainController -filter * | where{$_.site -ieq $site} | %{$_.name}
  • parse the client name from the event message
    $obj.Computer = [regex]::Match($_.message, "\d|\w+ failed").Value -ireplace " failed",""
  • make sure we only pick up a client name only once, so we have a unique list of clients with secure channel issues at the end:
    if($computerList -inotcontains $obj.Computer){
The full script with comments:
 param(      [string] $dcname = "",  
           [string] $site = "",  
           [int] $lastday = 2)  
 # if -dcname is not specified, but -site is, let's get the ist of DCs from that site  
 if(!$dcname -and $site){  
      Import-Module activedirectory  
      $dclist = Get-ADDomainController -filter * | where{$_.site -ieq $site} | %{$_.name}  
 }  
 else{  
      $dclist = @($dcname)  
 }  
 $dclist  
 # generate the start date for the eventlog query  
 $after = (Get-Date).adddays(-$lastday)  
 $objColl = $computerList = @()  
 if($dclist.length -gt 0){  
      foreach($srv in $dclist){  
           # get the netlogon 5805 events from the eventlog generated after the given date  
           Get-WinEvent -ea SilentlyContinue -ComputerName $srv -FilterHashtable @{LogName = "System"; StartTime = $after; id=5805} | %{  
                $obj = "" | select DC,Computer,message,Date  
                $obj.DC = $srv  
                # parse the computername from the event message  
                $obj.Computer = [regex]::Match($_.message, "\d|\w+ failed").Value -ireplace " failed",""  
                # if we haven't recorded the alert about the given computerm then record it  
                if($computerList -inotcontains $obj.Computer){  
                     $obj.message = $_.message.Split("`n")[1]  
                     $obj.Date = $_.TimeCreated  
                     # add the computername to an array where we can check if we have picked up an event on the given computer already  
                     $computerList += $obj.Computer  
                     $objColl += $obj  
                }  
           }  
      }  
 }  
 else{  
      Write-Host -ForegroundColor "red" "No DC or Site specified."  
 }  
 $objColl