+ Post New Thread
Page 1 of 3 123 LastLast
Results 1 to 15 of 35
Scripts Thread, Powershell 2: If [piped string] does not contain X, drop. If it does, pipe out. in Coding and Web Development; Furthering my quest to automate my many repeated tasks, I'm trying to write a script to query the event log ...
  1. #1


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207

    Powershell 2: If [piped string] does not contain X, drop. If it does, pipe out.

    Furthering my quest to automate my many repeated tasks, I'm trying to write a script to query the event log of remote machines and pull back a list of users that have logged on (mainly because the machine has been vandalised and the staff like to know the last few people to use it).. I've managed to narrow it down pretty well, it only pulls back Logon events, exactly as I tell it to. Thing is, it pulls back 'non-logon' logon events, mainly those caused by Services, such as our AV.

    So, what I need to do is now tell it 'If the Message does not contain the word 'winlogon', ignore the entire thing.', because the events for the AV logon, for example, come from the AV exe and not the winlogon exe. This should, in theory, make my script return only the actual user logons. However, after trying for a considerable amount of time, I'm pretty stumped. I've gotten this far with intuition and google-fu, but now even those are letting me down

    For those curious, my code is as follows:
    Code:
    $a = read-host "Computername?"
    # Set name of remote machine to query.
    $b = Read-Host "MaxEvents? (Default: 3)"
    if ($b -eq "") {($b = 3)}
    # Set amount of logs to return. If null, set to 3.
    
    Get-WinEvent -ComputerName "$a.domain.local" -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $b | Select Message | ForEach-Object {$_ -replace "(?s)Detailed Authentication .*? session key was requested.}", "------------------------------`r`n"} | Format-Table -Wrap -Auto | Out-File "M:\$aLog.txt"
    # Query eventlog of $a, return $b instances of Microsoft Windows Security Auditing logs with ID of 4624 (Logon). Strip out unnecessary description, replace with separator. Save to file.
    Start-Process "M:\$aLog.txt" -wait
    # Open file. Wait for file to close.
    Remove-Item "M:\$aLog.txt"
    # On file close, delete file.
    Edit: Point.. Has to be ran in Powershell 2.0, not 3.0. Powershell 3.0 always returns a null 'Message' column, a bug complained about by a lot of people online.
    Last edited by Garacesh; 16th September 2013 at 03:36 PM.

  2. #2
    pleach85's Avatar
    Join Date
    Jan 2011
    Posts
    22
    Thank Post
    3
    Thanked 6 Times in 6 Posts
    Rep Power
    9
    I think this will do what you want, though i'm no powershell guru myself so it's probably not the best way!

    Code:
    # Set computer name and add domain suffix
    $a = read-host "Computername?"
    $a += ".domain.local"
    
    # Set amount of logs to return. If null, set to 3.
    $b = Read-Host "MaxEvents? (Default: 3)"
    if ($b -eq "") {($b = 3)}
    
    # Set Logfile Location
    $logFile = "M:\" + $a + "Log.txt"
    
    # Get Logon Events and create array of messages
    $messages = @(Get-WinEvent -ComputerName "$a" -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $b | % {($_.message)})
    
    # Loop through messages and find ones with logon type 2 (interactive logon)
    foreach ($message in $messages){
        if ($message.contains("Logon Type:			2")){ 
            # Replace unwanted parts of the message and output the message to the logfile
            $message -replace "(?s)(Detailed)(.*)", "------------------------------`r`n" >> $logfile
        }
    }
    
    # Open file. Wait for file to close.
    Start-Process $logFile -wait
    
    # On file close, delete file.
    Remove-Item $logFile
    Last edited by pleach85; 17th September 2013 at 12:33 PM. Reason: Edited to reflect changes made in comment below

  3. #3


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207
    Aha! Certainly a step in the right direction. Thank you very much!

    So, since I'd like to understand it rather than simply use it.. Set hostname ($a) and append domain suffix (Didn't know you could do that, thanks!). I got that. Set amount of MaxEvents ($b) and if nul, set to 3. Got that.

    I take it what happens next is the '$messages' array is created from the output of the Get-WinEvent cmdlet, with the "% {($_.message)})" categorising them each as 'message'?
    'foreach ($message in $messages)' then checks each message (as defined by % {($_.message)})) in $messages for Logon Type: 2 and if it finds it, removes the unwanted bits and creates the log file (as specified via $logfile)

    However, due to '>' being Out-File (didn't know about that, nor % being ForEach-Object, so thanks!) it only gives me the last log if finds that meets the criteria, and not all the ones before it, because Out-File overwrites. Should I give it a large number or events to check, say, 100, it ought to return quite a few login records, but it doesn't.. So I tried changing > to '| Add-Content' and that gives me a lovely string of chinese-looking text, hardly ideal. Still, a definite step in the right direction.

    Aha! Figured it out! By creating another array ($logs) from the output of 'foreach [...]' and then '$logs | Out-File' it collects them all, and then writes the file, giving me the full record! Woohoo!

    A few adjustments have been made ("Logon Type: 2" changed to "C:\Windows\System32\winlogon.exe" as it was still returning irrelevant logs sometimes, and I've changed the $logfile = "M:\$aLog.txt" as it doesn't seem to like $aLog and simply creates the filed called .txt, so just 'Log.txt' will have to do) - not that I'm ungrateful. To learn, one has to adapt code rather than simply splice it.

    Code:
    # Set name of remote machine to query, add domain suffix.
    $a = read-host "Computername?"
    $a += ".domain.local"
    # Set amount of logs to return. If null, set to 3.
    $b = Read-Host "MaxEvents? (Default: 3)"
    if ($b -eq "") {($b = 3)}
    
    # Query eventlog of $a, return $b instances of Microsoft Windows Security Auditing logs with ID of 4624 (Logon). Create array.
    $messages = @(Get-WinEvent -ComputerName $a -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $b | % {($_.message)})
    
    # Loop through messages and find ones containing C:\...\winlogon.exe. Build new array.
    $logs = foreach ($message in $messages){
        if ($message.contains("C:\Windows\System32\winlogon.exe")){ 
            # Replace unwanted parts of the message and output the message to the logfile
            $message -replace "(?s)(Detailed)(.*)", "------------------------------`r`n`r`n`r`n------------------------------"
        }
    }
    
    # Write logs to file
    $logs | Out-File "M:\Log.txt"
    # Open file. Wait for file to close.
    Start-Process "M:\Log.txt" -wait
    # On file close, delete both files.
    Remove-Item "M:\Log.txt"
    Last edited by Garacesh; 17th September 2013 at 12:06 PM.

  4. #4
    pleach85's Avatar
    Join Date
    Jan 2011
    Posts
    22
    Thank Post
    3
    Thanked 6 Times in 6 Posts
    Rep Power
    9
    I agree, its how I learn most of my coding!

    Sounds like you've understood it perfectly, I've just made a few mistakes if you add >> instead of > that will append to the txt file instead of overwrite it. Also for the log file (didn't even notice it wasn't creating the proper text file name) you could use

    $logFile = "M:\" + $a + "Log.txt"

    This will build a proper string for the log file path.

  5. #5
    pleach85's Avatar
    Join Date
    Jan 2011
    Posts
    22
    Thank Post
    3
    Thanked 6 Times in 6 Posts
    Rep Power
    9
    Quote Originally Posted by Garacesh View Post
    I take it what happens next is the '$messages' array is created from the output of the Get-WinEvent cmdlet, with the "% {($_.message)})" categorising them each as 'message'?
    'foreach ($message in $messages)' then checks each message (as defined by % {($_.message)})) in $messages for Logon Type: 2 and if it finds it, removes the unwanted bits and creates the log file (as specified via $logfile)
    That's not exactly how it works... the messages array is created from the output of the Get-WinEvent cmdlet but it is not categorising them each as 'message' the % {($_.message)}) means foreach item passed into the pipe get the "message" part of the Get-WinEvent object.

    foreach ($message in $messages) could have easily have been foreach ($item in $messages) you are just creating a variable name for the value in the array.

  6. #6


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207
    Yeah, I figured similar when I attempted to tidy things up a bit more.. $a became $cn, $fqdn was created as $cn+domain (to keep the domain extension out of the log file name), $b became $me..

    However, When I changed the $_.message to $_.event and $messages to $logs (since I no longer needed the array $logs) the script broke. Seemed like it didn't want to accept different names.. I figured this might have been because the contents of the event log are referred to as 'Message' by Get-WinEvent so it had to be fixed (since there's no Select Message or similar in there), but once I'd changed them back, it worked again.

  7. #7


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207
    Sorry to be a nag - I'm trying to make it include the TimeCreated as well as the Message, but hitting a stump, since TimeCreated won't contain any filterable information like Message does.

    Is there a way I could link each instance of TimeCreated to Message before filtering them out (by the events that contain C:\...\Winlogon.exe)?

    Code:
    $messages = @(Get-WinEvent -ComputerName "$fqdn" -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $me | % {($_.message)})
    $times = @(Get-WinEvent -ComputerName "$fqdn" -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $me | % {($_.timecreated)})
    $fulllog = $messages + $times
    If I've gotten that right, that would end up with an array ($fulllog) of: Login; Login; Login; Timestamp; Timestamp; Timestamp; (x150) rather than Login+Timestamp; Login+Timestamp.
    I'm guessing I'd need to use some kind of foreach ($message in $messages) {combine with timecreated entry)..?
    Last edited by Garacesh; 18th September 2013 at 03:04 PM.

  8. #8
    pleach85's Avatar
    Join Date
    Jan 2011
    Posts
    22
    Thank Post
    3
    Thanked 6 Times in 6 Posts
    Rep Power
    9
    Rather than extracting them both separately and trying to merge them back together you'll be better off making an array of events then using a foreach to extract all the information you want from that event something like this...

    Code:
    $events = @(Get-WinEvent -ComputerName "$fqdn" -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $me)
    
    foreach ($event in $events){
    
        #You can do all the manipulation of the event here using things like
        # if ($event.message -contains "winlogon.exe"){ DO THIS}
    
        $fulllog += "----------" + $event.timecreated + "----------`r`n"
        $fulllog += $event.message + "`r`n`r`n"
    }
    This way you're only running the Get-WinEvent once and you are able to extract and modify all the information you want from the event then add it to your log in the foreach loop.

    Hope this helps!

  9. #9


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207
    Ohh.. So if multiple (I don't really know what to call them, so I'll call them) 'categories' of data are written into an array, they're carried into the array by their said category? (In this case, TimeCreated, Message, etc).. They keep that info?

    Good thing I don't have to calculate them both separately, that would've doubled the time the script takes! I'll play around with what you've given me and get back to you. Thanks!

  10. #10
    pleach85's Avatar
    Join Date
    Jan 2011
    Posts
    22
    Thank Post
    3
    Thanked 6 Times in 6 Posts
    Rep Power
    9
    Yeah, an array is a collection of items. The items in your array ($events) will be WinEvent-Objects as that's what you are putting in. Its these WinEvent objects that have all the information you need in them (TimeCreated, Messages etc). You can put any object into an array (string, int, another array, ...) it just so happens you're putting WinEvents in which contain alot of information you can extract later in your script.

  11. #11


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207
    Okay, I think I've gotten the theory of it.. But I seem to have gotten myself into a position where $FullLog is counting it all as one item, rather than multiple items of TimeCreated + Message and it's only pulling back one log..

    Code:
    # Query eventlog of $fqdn, return $me instances of Microsoft Windows Security Auditing logs with ID of 4624 (Logon). Create array.
    $events = @(Get-WinEvent -ComputerName "$fqdn" -FilterHashtable @{ProviderName="Microsoft-Windows-Security-Auditing"; ID="4624"} -MaxEvents $me)
    
    # Combine each message with its relevant timestamp
    foreach ($event in $events){
        $fulllogs += "----------" + $event.timecreated + "----------`r`n" + $event.message + "`r`n"
    }
    
    $fulllogs | Out-File M:\TESTFullLog1.txt
    
    # Loop through messages and find ones containing C:\...\winlogon.exe. Build new array.
    $logins = foreach ($fulllog in $fulllogs) {
        if ($fulllog.contains("C:\Windows\System32\winlogon.exe")){ 
            # Replace unwanted parts of the message and output the message to the logfile
            $fulllog -replace "(?s)(Detailed)(.*) key was requested.", "------------ End of event log ------------`r`n`r`n" 
    		$count = ([int]$count + 1)
        }
    }
    
    $fulllogs | Out-File M:\TESTFullLog2.txt
    
    # Write 
    "$count valid records within $me queried logon events. `r`n`r`n$logins" | Out-File $logFile
    Currently returns:

    Code:
    1 valid records within 150 queried logon events. 
    
    ----------09/19/2013 11:33:35----------
    An account was successfully logged on.
    
    Subject:
    	Security ID:		S-1-5-18
    	Account Name:		BL07-002$
    	Account Domain:		SMSC
    	Logon ID:		0x3e7
    
    Logon Type:			5
    
    New Logon:
    	Security ID:		S-1-5-21-2913182392-3483331520-2670013734-1004
    	Account Name:		SophosSAUBL07-0020
    	Account Domain:		BL07-002
    	Logon ID:		0x25f6157
    	Logon GUID:		{00000000-0000-0000-0000-000000000000}
    
    Process Information:
    	Process ID:		0x2bc
    	Process Name:		C:\ProgramData\Sophos\AutoUpdate\Cache\sophos_autoupdate1.dir\ALUpdate.exe
    
    Network Information:
    	Workstation Name:	BL07-002
    	Source Network Address:	-
    	Source Port:		-
    
    ------------ End of event log ------------
    That log should have been filtered out, because it doesn't contain the string "C:\Windows\System32\winlogon.exe", which is part of the reason I'm working on this.. Now, I've tried changing the replace from 'Detailed (to) (end of entry)' to 'Detailed (to) key requested' because I thought it was deleting all of the entries after the first one because it was all one item, but that doesn't seem to be doing the trick either.

    So, this is where M:\TESTFullLog1.txt and M:\TESTFullLog2.txt came in, one before and one after the filtering to ensure it was happening correctly, and right now they're both identical 579kb files, without the Detailed+ removed. So it's not working all of a sudden. But if it's not filtering them out properly, why am I getting one incorrect log and none of the rest?

    GAAHHH, why do I do this to myself?

  12. #12


    Join Date
    May 2009
    Posts
    2,943
    Thank Post
    259
    Thanked 773 Times in 588 Posts
    Rep Power
    284
    There is only one object in fulllogs, a massive string containing all the events. The line :

    $fulllogs += "----------" + $event.timecreated + "----------`r`n" + $event.message + "`r`n"

    Is just appending each time and message to that string. So your later code will find that that massive string does indeed contain "C:\Windows\System32\winlogon.exe".

  13. #13


    Join Date
    Jan 2012
    Posts
    2,585
    Thank Post
    926
    Thanked 344 Times in 264 Posts
    Rep Power
    207
    Yeah, I figured that's what was happening D:
    It formats them correctly, one timestamp, one message, one timestamp, one message.. but the fact that they're all one string doth complicate.

  14. #14


    Join Date
    May 2009
    Posts
    2,943
    Thank Post
    259
    Thanked 773 Times in 588 Posts
    Rep Power
    284
    It's useful to look at the events. In the ISE

    If you do something like :
    $events | export-csv c:\temp\test.csv

    You can then see each event object and it's properties.

    $events | get-member will show you any public methods

    All of which can be hehlpful in debugging and understanding what your code is actually doing.

  15. #15


    Join Date
    May 2009
    Posts
    2,943
    Thank Post
    259
    Thanked 773 Times in 588 Posts
    Rep Power
    284
    Just filter them when you filter the events into that string.

    foreach ($event in $events){
    if ($event.message.contains("blah")) {
    $fulllogs += "----------" + $event.timecreated + "----------`r`n" + $event.message + "`r`n"
    }
    }

SHARE:
+ Post New Thread
Page 1 of 3 123 LastLast

Similar Threads

  1. Looking for PHP blog script which does not require database
    By ajbritton in forum Web Development
    Replies: 10
    Last Post: 9th December 2010, 07:50 AM
  2. [Pics] But What Happens If This Windows 'Does Not Respond?'
    By DaveP in forum Jokes/Interweb Things
    Replies: 3
    Last Post: 1st December 2010, 11:00 AM
  3. SIMS .net does not fully support Open Office
    By mark in forum MIS Systems
    Replies: 24
    Last Post: 28th March 2008, 09:57 AM
  4. Replies: 10
    Last Post: 30th August 2007, 10:52 AM
  5. My domain does not exist?
    By Irazmus in forum Windows
    Replies: 17
    Last Post: 4th October 2006, 02:55 PM

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •