locked
Email events specified by event id with Powershell, and then keep doing so RRS feed

  • Question

  • Ok, many of you may have seen the progression of my questions about event logging.  It began with an entire vbscript, which had some issues.  Finally it was recommended to me to use PowerShell.  I have been reading everything I can about PowerShell since I heard of it a couple of days ago. 

    Here's what I am trying to do:

    - I want to have a script that will "watch" the event log for specified ID's (about 60(!))
    - When one of these events occurs, I would like to send an email with the info
    - I would like this to run continuously, although it can have up to a 15-30 minute pause

    I found something similar here: http://www.jdhitsolutions.com/resources/scripts/Report-Events.txt - I had it working, but only for one event, and sometimes not at all.

    I know there is probably a one line solution that will do what I need, but I am having problems figuring it out since I am so new at this.

    If anyone can help me out, I would appreciate it!

    Thanks,
    Michelle
    Wednesday, March 10, 2010 1:31 AM

Answers

  • Well, there was a typo in what I posted.  There shouldn't have been a " before $._instanceid.

    Having that quote there, and removing the one before the 7 is  bascically looking for all events with a source of "disk", but if those are the events you're wanting to alert on then your CSV file needs to look like this:

    Source,ID
    disk,3221487623

    • Marked as answer by IamMred Thursday, March 25, 2010 2:45 AM
    Wednesday, March 17, 2010 10:13 PM
  • What are you using for an smtp server?  If you don't have access to a mail relay, you won't be able to send email. 

    It wouldn't be hard to modify it to write to a log file, or you could use start-transcript, and run it with the -noemail and -showevents switches and capture what would be written to the console to a file.

    • Marked as answer by IamMred Thursday, March 25, 2010 2:44 AM
    Monday, March 22, 2010 8:51 PM

All replies

  • What OS are you running?
    Wednesday, March 10, 2010 1:39 AM
  • WinXP and Win2003 Server
    Wednesday, March 10, 2010 1:52 AM
  • There's been some improvements in the event log handling in Powershell V2 since that was written, so it can probably be made a little more reliable. 

    What kind of events are you looking for?
    Wednesday, March 10, 2010 2:00 AM
  • Well, I have a list of events based on some research.  They range across all four categories, so I need to specify by event id. 
    The vbscript that I tried and failed lists them all and are here: http://social.technet.microsoft.com/Forums/en-US/ITCG/thread/99276728-e6ff-459f-968a-98c4a2d6bfa0

    Wednesday, March 10, 2010 2:05 AM
  • Assuming these are all ISA events. start with this, and see if it gets you the events you're looking for:

    You may have to up the -Newest to get more events if they're sparse. 

    (run from the server)

    $evt_list = @"
    516
    517
    529
    530
    531
    532
    533
    534
    535
    537
    539
    548
    552
    608
    609
    611
    612
    617
    621
    622
    624
    625
    626
    627
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    641
    642
    643
    644
    646
    647
    661
    668
    671
    675
    676
    677
    681
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    695
    696
    600
    "@
    $evts = $evt_list.Split("`n")

    $logrecs = Get-EventLog -LogName "Security" -Newest 500 |? {$_.source -eq "ISA" -and $evt_list -contains $_.instanceid}

    $logrecs

    Wednesday, March 10, 2010 2:47 AM
  • Thank you.

    I have added in the email sending function and it works (which sadly took me a very long time to figure out the very basics)...
    What I am not sure how to do is actually send the event infomation and have it check every few minutes or so.

    $EmailFrom = "notification@logevent.com" 
    $EmailTo = "me@email.net"  
    $Subject = "Notification of..."  
    $Body = "this is a notification of..."  
    $SMTPServer = "smtp.server.net"  
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25)  
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("username", "password");  
    $SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
    Wednesday, March 10, 2010 5:27 AM
  • How do you want the event information to appear in the email?

    You can have summary information, the complete event information, you can have it in the body or as an attachment, or some combination.

    You can even send it as an xml file that can be imported back into Powershell for sorting or analysis.

    As far as running it, what's your desired run frequency?  You can run it as a scheduled task at intervals, but if the intervals get too frequent I think at some point the processor overhead of constatantly loading and unloading it gets to be more than if you ran it constantly and put it in a process/sleep/process loop.

     

     

    Wednesday, March 10, 2010 10:50 AM

  • When I was trying to use vbscript, it appeared this way as the body of an email:


    The time provider NtpClient is configured to acquire time from one or more time sources, however none of the sources are currently accessible.

    No attempt to contact a source will be made for 59 minutes.

    NtpClient has no source of accurate time.

     

    Time Written: 2/12/2010 11:40:37 PM

    Source Name: W32Time

    Event Type: 1

    Event ID: 29

    Category: 0

    Computer Name: ENG

    User:

    Record Number: 12662



    The only problem I had with this was that the category and event type came through as the numeric values.  This isn't a big deal if it will still appear this way.

    I would like to run it as often as possible without killing the computer.  I am planning to have a dedicated server to run this script and poll other computers in the network.  Anywhere up to a 30 minute interval, but the more often the better.  I would like to use the loop/sleep option.

    I really appreciate the help with this.  You will receive full credit for any information, etc. 

    Also, if anyone can recommend a good newbie resource for learning PowerShell 2.0, I would appreciate it!

    Wednesday, March 10, 2010 3:52 PM
  • Sorry I didn't get back to you sooner, but work happens.  I'll try to get back to this later this evening or tomorrow.  For the moment, the Powershell V1 Getting Started Guide is still available, and is a good basic primer.

    http://www.microsoft.com/downloads/details.aspx?FamilyID=b4720b00-9a66-430f-bd56-ec48bfca154f&displaylang=en
    Thursday, March 11, 2010 1:10 AM
  • Thank you - I will start going through this guide!
    Friday, March 12, 2010 3:09 PM
  • First installment:

    Create a new folder, copy this script into the folder along with two .txt files

    monitored_computers.txt = a list of the computer names you want to monitor
    alert_events.txt = that list of event ids you want to watch for

    The first time it runs, it should retrieve the latest 200 events (or whatever $seed_depth is set to).
    Subsequent runs should retrieve all the records since the last run.

    This will only report on the number of events processed, the number of alert events found, and the time it took to do it, per server.  We'll work on creating the alert emails next, but this will give us some idea what the timing loop needs to look like - you don't want to set the run frequency for less time than it takes to do a full process cycle.


    $computers = @(gc monitored_computers.txt)
    $events = @(gc alert_events.txt)

    write-host "Started pass on $(get-date)"

    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    $log = "System"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200


    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}
     
    $computers |%{
    $timer.start()
    Write-Host "Started processing $($_)"

    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    Write-Host "Processing $($n) events."
     
    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$_.source -eq "ISA" -and $evt_list -contains $_.instanceid}

     

    $loghist[$_] = $index
    $duration = $timer.elapsed
    $timer.Reset()

    if ($log_hits){$hits = $log_hits.count}
     else {$hits = 0}
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "

    }

    $loghist | export-clixml $hist_file


    write-host "Ended pass on $(get-date)"

    • Edited by mjolinor Saturday, March 13, 2010 3:01 PM copy paste error. - missed the tail end of the process loop.
    Saturday, March 13, 2010 6:48 AM
  • Second installment.  Added process/sleep/process loop, comments, and someplace to credit anybody who wants to add to this contraption.

    ## Powershell Log Monitor Script ##
    ## Contributing authors - mjolinor,

    $log = "System"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200

    #run interval in minutes - set to zero for runonce.
    $run_interval = 0


    $computers = @(gc monitored_computers.txt)
    $event_list = @(gc alert_events.txt)

    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}


    $timer = [System.Diagnostics.Stopwatch]::StartNew()

    #START OF RUN PASS
    $run_pass = {

    $computers |%{
    $timer.reset()
    $timer.start()

    Write-Host "Started processing $($_)"

    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    Write-Host "Processing $($n) events."

    #get the log entries
    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$_.source -eq "ISA" -and $evt_list -contains $_.instanceid}

    #save the current index to $loghist for the next pass
    $loghist[$_] = $index

    #report number of alert events found and how long it took to do it
    if ($log_hits){$hits = $log_hits.count}
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    }

    #save the history file to disk for next script run
    $loghist | export-clixml $hist_file
    }
    #END OF RUN PASS

    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"

    #run the first pass
    $start_pass = Get-Date
    &$run_pass

    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date 
     &$run_pass
     }
     

     

     

     

     

     

    Saturday, March 13, 2010 6:10 PM
  • I really appreciate you taking the time for this.  I am looking forward to the day I will be answering instead of asking questions!

    I would like to credit you in my thesis - may I use your full name?  If so, just let me know what it is. 

    (If anyone has changes/additions, I will credit you as well)
    Saturday, March 13, 2010 7:58 PM
  • Let's see I can make it work before we start putting my name on it. 
    I added your name as a contributor.  If the answer turns out to be useful to anyone else, you get credit for asking the right question.

    This is not fully tested, so let me know if it does anything unexpected.

    ## Powershell Log Monitor Script ##
    ## Contributing authors - mck74,mjolinor

    $log = "System"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200

    #run interval in minutes - set to zero for runonce.
    $run_interval = 0

    $EmailFrom = "<user@domain.tld>"
    $EmailTo = "<user@domain.tld>"
    $EmailSubject = "Server event notification" 
     
    $SMTPServer = "smtp.server.net"
    $EMailAuthUsername = "username"
    $EmailAuthPassword = "password"

    $computers = @(gc monitored_computers.txt)
    $event_list = @(gc alert_events.txt)

    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}


    $timer = [System.Diagnostics.Stopwatch]::StartNew()

    #START OF RUN PASS
    $run_pass = {

    $EmailBody = "Log monitor found monitored events. 'n"

    $computers |%{
    $timer.reset()
    $timer.start()

    Write-Host "Started processing $($_)"

    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    Write-Host "Processing $($n) events."

    #get the log entries
    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$_.source -eq "ISA" -and $evt_list -contains $_.instanceid}

    #save the current index to $loghist for the next pass
    $loghist[$_] = $index

    #Add server name and monitored events found to email body.
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String
     }
     else {$hits = 0}

    #report number of alert events found and how long it took to do it
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    }

    #save the history file to disk for next script run
    $loghist | export-clixml $hist_file

    #Send email if there were any monitored events found
    if ($events_found){send_email}

    }
    #END OF RUN PASS

    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"

    #run the first pass
    $start_pass = Get-Date
    &$run_pass

    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date 
     &$run_pass
     }
     
    function send_email {
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25) 
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("$EmailAuthUsername", "$EmailAuthPassword")
    $SMTPClient.Send($EmailFrom, $EmailTo, $EmailSubject, $EmailBody)
    }

     

    Sunday, March 14, 2010 6:32 AM
  • I mucked with it a little more, and made some changes.

    Moved the send_email fuction up (duh!)

    I didn't like that the event source was hard-coded, so I changed the monitored_events file to a .csv.
    It now uses monitored_events.csv to specify source/id pairs that it will alert on.

    I added a couple of switch parameters.
    -showevents will display the monitored events found for each server along with the count and timer stats.
    -noemail will suppress sending emails.

    Made a couple of minor changes to variable names for consistency.

    I don't have access to an smtp relay right now, so I can't really test it all.  If it tests out OK, we'll put it in the Script Center.

    ## Powershell Log Monitor Script ##
    ## Contributing authors - mck74, mjolinor,

    param([switch]$ShowEvents = $false,[switch]$NoEmail = $false)


    $log = "Security"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200

    #run interval in minutes - set to zero for runonce.
    $run_interval = 0

    $EmailFrom = "<user@domain.tld>"
    $EmailTo = "<user@domain.tld>"
    $EmailSubject = "Server event notification" 
     
    $SMTPServer = "smtp.server.net"
    $SMTPAuthUsername = "username"
    $SMTPAuthPassword = "password"

    $computers = @(gc monitored_computers.txt)
    $event_list = @{}
    Import-Csv alert_events.csv |% {$event_list[$_.source + '#' + $_.id] = 1}


    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}


    $timer = [System.Diagnostics.Stopwatch]::StartNew()

    function send_email {
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25) 
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("$SMTPAuthUsername", "$SMTPAuthPassword")
    $SMTPClient.Send($EmailFrom, $EmailTo, $EmailSubject, $EmailBody)
    }
    #START OF RUN PASS
    $run_pass = {

    $EmailBody = "Log monitor found monitored events. `n"

    $computers |%{
    $timer.reset()
    $timer.start()

    Write-Host "Started processing $($_)"

    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    Write-Host "Processing $($n) events."

    #get the log entries

    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.instanceid]}

    #save the current index to $loghist for the next pass
    $loghist[$_] = $index

    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String
     }
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    if ($ShowEvents){$log_hits | fl | Out-String}
    }

    #save the history file to disk for next script run
    $loghist | export-clixml $hist_file

    #Send email if there were any monitored events found
    if ($events_found -and -not $NoEmail){send_email}

    }
    #END OF RUN PASS

    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"

    #run the first pass
    $start_pass = Get-Date
    &$run_pass

    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date 
     &$run_pass
     }

    Sunday, March 14, 2010 6:13 PM
  • Ok, I am getting a couple of errors.  I am sure it is something I didn't name/edit correctly.  Here is the error:

    ************************************************************
    Log monitor started at 03/14/2010 16:14:03
    ************************************************************
    
    Started processing LISW7499ZLT
    Get-EventLog : The network path was not found.
    At C:\Users\user\Desktop\ThesisScript\script.ps1:51 char:23
    + $index = (Get-EventLog <<<<  -ComputerName $_ -LogName $log -newest 1).index
        + CategoryInfo          : NotSpecified: (:) [Get-EventLog], IOException
        + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Comma
       nds.GetEventLogCommand
    
    Processing 200 events.
    Get-EventLog : The network path was not found.
    At C:\Users\user\Desktop\ThesisScript\script.ps1:62 char:25
    + $log_hits = Get-EventLog <<<<  -ComputerName $_ -LogName $log -Newest $n |
        + CategoryInfo          : NotSpecified: (:) [Get-EventLog], IOException
        + FullyQualifiedErrorId : System.IO.IOException,Microsoft.PowerShell.Comma
       nds.GetEventLogCommand
    
    Found 0 alert events in 0.0678081 seconds.
    ------------------------------------------------------------
    
    
    ********** Sleeping for 59.9219999 seconds
    (I do have the alert_events.csv and monitored_computers.tx in the same directory)
    Sunday, March 14, 2010 8:16 PM
  • According to this:

    Started processing LISW7499ZLT
    Get-EventLog : The network path was not found.

    It's not finding that computer on your network.
    Sunday, March 14, 2010 8:23 PM
  • I'm not sure why... I re-typed it in and it's happily running now.

    Now I am not getting any events that trigger the alert.  I am specifically doing things to cause these specific event ID's, such as clearing the event log.  The event is in the log and in the csv file, however I keep getting this:

    ************************************************************
    Log monitor started at 03/14/2010 17:00:55
    ************************************************************
    
    Started processing win2k3cdx08
    Processing 243 events.
    Found 0 alert events in 0.1079885 seconds.
    ------------------------------------------------------------
    Sunday, March 14, 2010 9:05 PM
  • What does your .csv look like?
    Sunday, March 14, 2010 9:13 PM
  • It's just listing the event ID's, but I just realized you said source/id pairs. *facepalm*  How do you have that listed in the csv file?
    Sunday, March 14, 2010 9:16 PM
  • Two columns.  One labeled Source, and one labeled ID.  You can do it up in Excel, and save as type MS-DOS csv.
    Sunday, March 14, 2010 9:19 PM
  • Yay it works.  :)

    Now I need to see how I can format the email, etc.



    Log monitor found monitored events. 
    
     Alert Events on server win2k3cdx08.thisismytestdomain
    
    
    Index              : 47
    EntryType          : SuccessAudit
    InstanceId         : 552
    Message            : Logon attempt using explicit credentials:
                         
                         Logged on user:
                         
                             User Name:    WIN2K3CDX08$
                         
                             Domain:        THISISMYTESTDOM
                         
                             Logon ID:        (0x0,0x3E7)
                         
                             Logon GUID:    -
                         
                         User whose credentials were used:
                         
                             Target User Name:    Administrator
                         
                             Target Domain:    THISISMYTESTDOM
                         
                             Target Logon GUID: -
                         
                         
                         Target Server Name:    localhost
                         
                         Target Server Info:    localhost
                         
                         Caller Process ID:    868
                         
                         Source Network Address:    -
                         
                         Source Port:    -
                         
    Category           : Logon/Logoff
    CategoryNumber     : 2
    ReplacementStrings : {WIN2K3CDX08$, THISISMYTESTDOM, (0x0,0x3E7), -...}
    Source             : Security
    TimeGenerated      : 3/14/2010 5:58:18 PM
    TimeWritten        : 3/14/2010 5:58:18 PM
    UserName           : NT AUTHORITY\SYSTEM
    
    Index              : 42
    EntryType          : SuccessAudit
    InstanceId         : 552
    Message            : Logon attempt using explicit credentials:
                         
                         Logged on user:
                         
                             User Name:    WIN2K3CDX08$
                         
                             Domain:        THISISMYTESTDOM
                         
                             Logon ID:        (0x0,0x3E7)
                         
                             Logon GUID:    -
                         
                         User whose credentials were used:
                         
                             Target User Name:    Administrator
                         
                             Target Domain:    THISISMYTESTDOM
                         
                             Target Logon GUID: -
                         
                         
                         Target Server Name:    localhost
                         
                         Target Server Info:    localhost
                         
                         Caller Process ID:    868
                         
                         Source Network Address:    -
                         
                         Source Port:    -
                         
    Category           : Logon/Logoff
    CategoryNumber     : 2
    ReplacementStrings : {WIN2K3CDX08$, THISISMYTESTDOM, (0x0,0x3E7), -...}
    Source             : Security
    TimeGenerated      : 3/14/2010 5:58:18 PM
    TimeWritten        : 3/14/2010 5:58:18 PM
    UserName           : NT AUTHORITY\SYSTEM
    
    Sunday, March 14, 2010 10:01 PM
  • Cool.  How's it hitting the cpu?  IIRC, that was one of the original problems you were trying to resolve by using PS instead of the VB script.
    Sunday, March 14, 2010 10:12 PM
  • Make this change to filter out all the whitespace lines from the event in the email body:

    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String |? {$_}      <=======
     }
    Sunday, March 14, 2010 10:19 PM
  • Ack.  Need to change it in two places to clean up the email and the display.

    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String |? {$_}      <======
     }
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    if ($ShowEvents){$log_hits | fl | Out-String |? {$_}}     <======
    }
    Sunday, March 14, 2010 10:24 PM
  • It's not too bad at all on the CPU.  I do get some strange errors every few runs though:


    ********** Sleeping for 59.984375 seconds
    
    Started processing win2k3cdx08.thisismytestdomain
    Processing 4 events.
    Found 0 alert events in 0.023194 seconds.
    ------------------------------------------------------------
    
    
    ********** Sleeping for 59.96875 seconds
    
    Started processing win2k3cdx08.thisismytestdomain
    Processing -777 events.
    Get-EventLog : Cannot validate argument on parameter 'Newest'. The -777 argument is less than the minimum allowed range
     of 0. Supply an argument that is greater than 0 and then try the command again.
    At C:\Documents and Settings\Administrator\Desktop\ThesisScript\hepl.ps1:62 char:64
    + $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest <<<<  $n |
        + CategoryInfo          : InvalidData: (:) [Get-EventLog], ParameterBindingValidationException
        + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.GetEventLogCommand
    
    Found 0 alert events in 0.0529357 seconds.
    ------------------------------------------------------------
    
    
    ********** Sleeping for 59.9375 seconds
    Sunday, March 14, 2010 10:51 PM
  • You're getting a negative index delta.  That's just wrong. 

    What's your run interval?
    Sunday, March 14, 2010 10:58 PM
  • I changed it to 1. 

    I haven't tried running it any shorter (because I don't know how) :) or longer.

    I tried .50, but it just said it was waiting for 59 seconds anyway.
    Sunday, March 14, 2010 11:02 PM
  • Make this change, and let's see what those index numer look like:

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){
     $n = $index - $loghist[$_]
     Write-Host "`nCurrent index is $($index)'nLogist index is $($loghist[$_])`n"
     }
    else {$n = $seed_depth}

    Sunday, March 14, 2010 11:13 PM
  • I think I just figured it out.  Earlier you said:

    "Now I am not getting any events that trigger the alert.  I am specifically doing things to cause these specific event ID's, such as clearing the event log.  The event is in the log and in the csv file, however I keep getting this:"

    I think your clearing the event log is resetting the index, and making it come up with an negative index delta between runs.

    Sunday, March 14, 2010 11:22 PM
  • Do this instead:

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    if ($n -lt 0){
     Write-Host "Log index changed since last run. The log may have been cleared. Re-seeding index."
     $n = $seed_depth
     }
     
    Write-Host "Processing $($n) events."
    Sunday, March 14, 2010 11:33 PM
  • You're right.  It works fine until I clear the log.  Unfortunately, this is a big issue for me since I am focusing on "insider threat" and clearing a log is a big deal.
    Sunday, March 14, 2010 11:35 PM
  • That fixed it!
    Sunday, March 14, 2010 11:59 PM
  • You're right.  It works fine until I clear the log.  Unfortunately, this is a big issue for me since I am focusing on "insider threat" and clearing a log is a big deal.

    Then you probably want this:

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    if ($n -lt 0){
     Write-Host "Log index changed since last run. The log may have been cleared. Re-seeding index."
     $events_found = $true
     $EmailBody += "`n Possible Log Reset $($_)`nEvent Index reset detected by Log Monitor`n"
     $n = $seed_depth
     }

    If they reset the log and then try to backfill it with innocuous looking events you might miss the reset showing up on the next pass.

    Monday, March 15, 2010 12:13 AM
  • This works great! 
    Monday, March 15, 2010 12:28 AM
  • How long is it taking it to make a full pass?
    Monday, March 15, 2010 12:33 AM
  • This is what I have after the last round of updates:

    ## Powershell Log Monitor Script ##
    ## Contributing authors - mck74,mjolinor,

    param([switch]$ShowEvents = $false,[switch]$NoEmail = $false)


    $log = "Security"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200

    #run interval in minutes - set to zero for runonce.
    $run_interval = 0

    $EmailFrom = "<user@domain.tld>"
    $EmailTo = "<user@domain.tld>"
    $EmailSubject = "Server event notification" 
     
    $SMTPServer = "smtp.server.net"
    $SMTPAuthUsername = "username"
    $SMTPAuthPassword = "password"

    $computers = @(gc monitored_computers.txt)
    $event_list = @{}
    Import-Csv alert_events.csv |% {$event_list[$_.source + '#' + $_.id] = 1}


    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}


    $timer = [System.Diagnostics.Stopwatch]::StartNew()

    function send_email {
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25) 
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("$SMTPAuthUsername", "$SMTPAuthPassword")
    $SMTPClient.Send($EmailFrom, $EmailTo, $EmailSubject, $EmailBody)
    }
    #START OF RUN PASS
    $run_pass = {

    $EmailBody = "Log monitor found monitored events. `n"

    $computers |%{
    $timer.reset()
    $timer.start()

    Write-Host "Started processing $($_)"

    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    if ($n -lt 0){
     Write-Host "Log index changed since last run. The log may have been cleared. Re-seeding index."
     $events_found = $true
     $EmailBody += "`n Possible Log Reset $($_)`nEvent Index reset detected by Log Monitor`n"
     $n = $seed_depth
     }
     
    Write-Host "Processing $($n) events."

    #get the log entries

    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.instanceid]}

    #save the current index to $loghist for the next pass
    $loghist[$_] = $index

    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String |? {$_}
     }
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    if ($ShowEvents){$log_hits | fl | Out-String |? {$_}}
    }

    #save the history file to disk for next script run
    $loghist | export-clixml $hist_file

    #Send email if there were any monitored events found
    if ($events_found -and -not $NoEmail){send_email}

    }
    #END OF RUN PASS

    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"

    #run the first pass
    $start_pass = Get-Date
    &$run_pass

    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date 
     &$run_pass
     }
     

     

     

    Monday, March 15, 2010 12:39 AM
  • Here is a test run:

    ************************************************************
    Log monitor started at 03/14/2010 21:30:09
    ************************************************************
    
    Started processing win2k3cdx08.thisismytestdomain
    Processing 610 events.
    Found 4 alert events in 0.5495134 seconds.
    ------------------------------------------------------------
    
    
    ********** Sleeping for 58.125 seconds
    
    Started processing win2k3cdx08.thisismytestdomain
    Processing 5 events.
    Found 0 alert events in 0.130088 seconds.
    ------------------------------------------------------------
    
    
    ********** Sleeping for 59.859375 seconds
    
    Started processing win2k3cdx08.thisismytestdomain
    Log index changed since last run. The log may have been cleared. Re-seeding index.
    Processing 200 events.
    Found  alert events in 0.0883578 seconds.
    ------------------------------------------------------------
    
    
    ********** Sleeping for 59.328125 seconds
    Monday, March 15, 2010 1:33 AM
  • Looks like you might want to increase the initial seed depth, but it seems to be working (at least on that one server).  Have you tested sending email?
    Monday, March 15, 2010 1:41 AM
  • Yes, here is a sample email:

    Log monitor found monitored events. 
    
     Alert Events on server win2k3cdx08.thisismytestdomain
    
    
    Index              : 601
    EntryType          : SuccessAudit
    InstanceId         : 552
    Message            : Logon attempt using explicit credentials:
                         
                         Logged on user:
                         
                             User Name:    WIN2K3CDX08$
                         
                             Domain:        THISISMYTESTDOM
                         
                             Logon ID:        (0x0,0x3E7)
                         
                             Logon GUID:    -
                         
                         User whose credentials were used:
                         
                             Target User Name:    Administrator
                         
                             Target Domain:    THISISMYTESTDOM
                         
                             Target Logon GUID: -
                         
                         
                         Target Server Name:    localhost
                         
                         Target Server Info:    localhost
                         
                         Caller Process ID:    868
                         
                         Source Network Address:    -
                         
                         Source Port:    -
                         
    Category           : Logon/Logoff
    CategoryNumber     : 2
    ReplacementStrings : {WIN2K3CDX08$, THISISMYTESTDOM, (0x0,0x3E7), -...}
    Source             : Security
    TimeGenerated      : 3/14/2010 9:29:19 PM
    TimeWritten        : 3/14/2010 9:29:19 PM
    UserName           : NT AUTHORITY\SYSTEM
    
    Index              : 537
    EntryType          : SuccessAudit
    InstanceId         : 552
    Message            : Logon attempt using explicit credentials:
                         
                         Logged on user:
                         
                             User Name:    WIN2K3CDX08$
                         
                             Domain:        THISISMYTESTDOM
                         
                             Logon ID:        (0x0,0x3E7)
                         
                             Logon GUID:    -
                         
                         User whose credentials were used:
                         
                             Target User Name:    Administrator
                         
                             Target Domain:    THISISMYTESTDOM
                         
                             Target Logon GUID: {11026ccd-1068-433b-697e-077d07dd057e}
                         
                         
                         Target Server Name:    localhost
                         
                         Target Server Info:    localhost
                         
                         Caller Process ID:    332
                         
                         Source Network Address:    127.0.0.1
                         
                         Source Port:    0
                         
    Category           : Logon/Logoff
    CategoryNumber     : 2
    ReplacementStrings : {WIN2K3CDX08$, THISISMYTESTDOM, (0x0,0x3E7), -...}
    Source             : Security
    TimeGenerated      : 3/14/2010 9:28:37 PM
    TimeWritten        : 3/14/2010 9:28:37 PM
    UserName           : NT AUTHORITY\SYSTEM
    
    Index              : 11
    EntryType          : SuccessAudit
    InstanceId         : 552
    Message            : Logon attempt using explicit credentials:
                         
                         Logged on user:
                         
                             User Name:    WIN2K3CDX08$
                         
                             Domain:        THISISMYTESTDOM
                         
                             Logon ID:        (0x0,0x3E7)
                         
                             Logon GUID:    -
                         
                         User whose credentials were used:
                         
                             Target User Name:    Administrator
                         
                             Target Domain:    THISISMYTESTDOM
                         
                             Target Logon GUID: -
                         
                         
                         Target Server Name:    localhost
                         
                         Target Server Info:    localhost
                         
                         Caller Process ID:    864
                         
                         Source Network Address:    -
                         
                         Source Port:    -
                         
    Category           : Logon/Logoff
    CategoryNumber     : 2
    ReplacementStrings : {WIN2K3CDX08$, THISISMYTESTDOM, (0x0,0x3E7), -...}
    Source             : Security
    TimeGenerated      : 3/14/2010 8:26:10 PM
    TimeWritten        : 3/14/2010 8:26:10 PM
    UserName           : NT AUTHORITY\SYSTEM
    
    Index              : 4
    EntryType          : SuccessAudit
    InstanceId         : 552
    Message            : Logon attempt using explicit credentials:
                         
                         Logged on user:
                         
                             User Name:    WIN2K3CDX08$
                         
                             Domain:        THISISMYTESTDOM
                         
                             Logon ID:        (0x0,0x3E7)
                         
                             Logon GUID:    -
                         
                         User whose credentials were used:
                         
                             Target User Name:    Administrator
                         
                             Target Domain:    THISISMYTESTDOM
                         
                             Target Logon GUID: -
                         
                         
                         Target Server Name:    localhost
                         
                         Target Server Info:    localhost
                         
                         Caller Process ID:    864
                         
                         Source Network Address:    -
                         
                         Source Port:    -
                         
    Category           : Logon/Logoff
    CategoryNumber     : 2
    ReplacementStrings : {WIN2K3CDX08$, THISISMYTESTDOM, (0x0,0x3E7), -...}
    Source             : Security
    TimeGenerated      : 3/14/2010 8:26:09 PM
    TimeWritten        : 3/14/2010 8:26:09 PM
    UserName           : NT AUTHORITY\SYSTEM
    Monday, March 15, 2010 1:48 AM
  • One of the last updates was supposed to get rid of the whitespace in the email body.  Do you have this?

    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String |? {$_}     <==============
     }

    Monday, March 15, 2010 2:03 AM
  • I thought I did.  Here is the entire script I have:

    ## Powershell Log Monitor Script ##
    ## Contributing authors - mck74, mjolinor,
    
    param([switch]$ShowEvents = $false,[switch]$NoEmail = $false)
    
    
    $log = "Security"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200
    
    #run interval in minutes - set to zero for runonce.
    $run_interval = 1
    
    $EmailFrom = "<logevent@alert.com>"
    $EmailTo = "<spirit@email.net>"
    $EmailSubject = "Server event notification"  
      
    $SMTPServer = "smtp.email.net"
    $SMTPAuthUsername = "user"
    $SMTPAuthPassword = "password"
    
    $computers = @(gc monitored_computers.txt)
    $event_list = @{}
    Import-Csv alert_events.csv |% {$event_list[$_.source + '#' + $_.id] = 1}
    
    
    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}
    
    
    $timer = [System.Diagnostics.Stopwatch]::StartNew()
    
    function send_email {
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25)  
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("$SMTPAuthUsername", "$SMTPAuthPassword") 
    $SMTPClient.Send($EmailFrom, $EmailTo, $EmailSubject, $EmailBody)
    }
    #START OF RUN PASS
    $run_pass = {
    
    $EmailBody = "Log monitor found monitored events. `n"
    
    $computers |%{
    $timer.reset()
    $timer.start()
    
    Write-Host "Started processing $($_)"
    
    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index
    
    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
     if ($n -lt 0){
     Write-Host "Log index changed since last run. The log may have been cleared. Re-seeding index."
     $events_found = $true
     $EmailBody += "`n Possible Log Reset $($_)`nEvent Index reset detected by Log Monitor`n"
     $n = $seed_depth
     }
     
    Write-Host "Processing $($n) events."
    
    #get the log entries
    
    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.instanceid]}
    
    #save the current index to $loghist for the next pass
    $loghist[$_] = $index
    
    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $EmailBody += $log_hits | fl | Out-String |? {$_}
     }
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    if ($ShowEvents){$log_hits | fl | Out-String}
    }
    
    #save the history file to disk for next script run 
    $loghist | export-clixml $hist_file
    
    #Send email if there were any monitored events found
    if ($events_found -and -not $NoEmail){send_email}
    
    }
    #END OF RUN PASS
    
    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"
    
    #run the first pass
    $start_pass = Get-Date
    &$run_pass
    
    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date 
     &$run_pass
     }
    Monday, March 15, 2010 2:55 AM
  • Looks like you got it.  I'd expect those emails to look better than that.  I'll check it out when I get to work tomorrow.
    Monday, March 15, 2010 3:57 AM
  • See if this produces better looking emai:

    ## Powershell Log Monitor Script ##
    ## Contributing authors - mck74,mjolinor,

    param([switch]$ShowEvents = $false,[switch]$NoEmail = $false)


    $log = "Security"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200

    #run interval in minutes - set to zero for runonce.
    $run_interval = 1

    $EmailFrom = "<logevent@alert.com>"
    $EmailTo = "<spirit@email.net>"
    $EmailSubject = "Server event notification" 
     
    $SMTPServer = "smtp.email.net"
    $SMTPAuthUsername = "user"
    $SMTPAuthPassword = "password"


    $computers = @(gc monitored_computers.txt)
    $event_list = @{}
    Import-Csv alert_events.csv |% {$event_list[$_.source + '#' + $_.id] = 1}


    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}


    $timer = [System.Diagnostics.Stopwatch]::StartNew()

    function send_email {
    $mailmessage = New-Object system.net.mail.mailmessage
    $mailmessage.from = ($emailfrom)
    $mailmessage.To.add($emailto)
    $mailmessage.Subject = $emailsubject
    $mailmessage.Body = $emailbody
    $mailmessage.IsBodyHTML = $true
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25) 
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("$SMTPAuthUsername", "$SMTPAuthPassword")
    $SMTPClient.Send($mailmessage)
    }
    #START OF RUN PASS
    $run_pass = {

    $EmailBody = "Log monitor found monitored events. `n"

    $computers |%{
    $timer.reset()
    $timer.start()

    Write-Host "Started processing $($_)"

    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    if ($n -lt 0){
     Write-Host "Log index changed since last run. The log may have been cleared. Re-seeding index."
     $events_found = $true
     $EmailBody += "`n Possible Log Reset $($_)`nEvent Index reset detected by Log Monitor`n" | ConvertTo-Html
     $n = $seed_depth
     }
     
    Write-Host "Processing $($n) events."

    #get the log entries

    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.instanceid]}

    #save the current index to $loghist for the next pass
    $loghist[$_] = $index

    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $log_hits |%{
      $emailbody += "<br><br>"
      $emailbody += $_ | select MachineName,EventID,Message | ConvertTo-Html
     $emailbody += "<br><br>"
     }
     }
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    if ($ShowEvents){$log_hits | fl | Out-String |? {$_}}
    }

    #save the history file to disk for next script run
    $loghist | export-clixml $hist_file

    #Send email if there were any monitored events found
    if ($events_found -and -not $NoEmail){send_email}

    }
    #END OF RUN PASS

    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"

    #run the first pass
    $start_pass = Get-Date
    &$run_pass

    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date
     &$run_pass
     }

     

    • Marked as answer by mck74 Monday, March 15, 2010 9:42 PM
    • Unmarked as answer by mck74 Monday, March 15, 2010 10:21 PM
    Monday, March 15, 2010 5:40 PM
  • That looks great!
    Monday, March 15, 2010 9:42 PM
  • Last installment, I think.  I cleaned up the email addresses, and added an option for 0 delay continuous loop.

    Unless you have some objection, I think we can call it done and send it off to the ScriptCenter for posterity.





    ## Powershell Log Monitor Script ##
    ## Contributing authors - mck74,mjolinor,

    param([switch]$ShowEvents = $false,[switch]$NoEmail = $false)


    $log = "Security"
    $hist_file = $log + "_loghist.xml"
    $seed_depth = 200

    #run interval in minutes - set to zero for runonce, "C" for 0 delay continuous loop.
    $run_interval = 0

    $EmailFrom = "<user@domain.tld>"
    $EmailTo = "<user@domain.tld>"
    $EmailSubject = "Server event notification" 
     
    $SMTPServer = "smtphost.domain.tld"
    $SMTPAuthUsername = "username"
    $SMTPAuthPassword = "password"

    $computers = @(gc monitored_computers.txt)
    $event_list = @{}
    Import-Csv alert_events.csv |% {$event_list[$_.source + '#' + $_.id] = 1}


    #see if we have a history file to use, if not create an empty $histlog
    if (Test-Path $hist_file){$loghist = Import-Clixml $hist_file}
     else {$loghist = @{}}


    $timer = [System.Diagnostics.Stopwatch]::StartNew()

    function send_email {
    $mailmessage = New-Object system.net.mail.mailmessage
    $mailmessage.from = ($emailfrom)
    $mailmessage.To.add($emailto)
    $mailmessage.Subject = $emailsubject
    $mailmessage.Body = $emailbody
    $mailmessage.IsBodyHTML = $true
    $SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 25) 
    $SMTPClient.Credentials = New-Object System.Net.NetworkCredential("$SMTPAuthUsername", "$SMTPAuthPassword")
    $SMTPClient.Send($mailmessage)
    }
    #START OF RUN PASS
    $run_pass = {

    $EmailBody = "Log monitor found monitored events. `n"

    $computers |%{
    $timer.reset()
    $timer.start()

    Write-Host "Started processing $($_)"

    #Get the index number of the last log entry
    $index = (Get-EventLog -ComputerName $_ -LogName $log -newest 1).index

    #if we have a history entry calculate number of events to retrieve
    #   if we don't have an event history, use the $seed_depth to do initial seeding
    if ($loghist[$_]){$n = $index - $loghist[$_]}
     else {$n = $seed_depth}
     
    if ($n -lt 0){
     Write-Host "Log index changed since last run. The log may have been cleared. Re-seeding index."
     $events_found = $true
     $EmailBody += "`n Possible Log Reset $($_)`nEvent Index reset detected by Log Monitor`n" | ConvertTo-Html
     $n = $seed_depth
     }
     
    Write-Host "Processing $($n) events."

    #get the log entries

    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.instanceid]}

    #save the current index to $loghist for the next pass
    $loghist[$_] = $index

    #report number of alert events found and how long it took to do it
    if ($log_hits){
     $events_found = $true
     $hits = $log_hits.count
     $EmailBody += "`n Alert Events on server $($_)`n"
     $log_hits |%{
      $emailbody += "<br><br>"
      $emailbody += $_ | select MachineName,EventID,Message | ConvertTo-Html
     $emailbody += "<br><br>"
     }
     }
     else {$hits = 0}
    $duration = ($timer.elapsed).totalseconds
    write-host "Found $($hits) alert events in $($duration) seconds."
    "-"*60
    " "
    if ($ShowEvents){$log_hits | fl | Out-String |? {$_}}
    }

    #save the history file to disk for next script run
    $loghist | export-clixml $hist_file

    #Send email if there were any monitored events found
    if ($events_found -and -not $NoEmail){send_email}

    }
    #END OF RUN PASS

    Write-Host "`n$("*"*60)"
    Write-Host "Log monitor started at $(get-date)"
    Write-Host "$("*"*60)`n"

    #run the first pass
    $start_pass = Get-Date
    &$run_pass

    #if $run_interval is set, calculate how long to sleep before the next pass
    while ($run_interval -gt 0){
    if ($run_interval -eq "C"){&$run_pass}
     else{
     $last_run = (Get-Date) - $start_pass
     $sleep_time = ([TimeSpan]::FromMinutes($run_interval) - $last_run).totalseconds
     Write-Host "`n$("*"*10) Sleeping for $($sleep_time) seconds `n"
     
    #sleep, and then start the next pass
     Start-Sleep -seconds $sleep_time
     $start_pass = Get-Date
     &$run_pass
     }
     }

     

    • Marked as answer by mck74 Monday, March 15, 2010 10:21 PM
    • Unmarked as answer by mck74 Tuesday, March 16, 2010 12:52 AM
    Monday, March 15, 2010 9:47 PM
  • Here is an example of the email I receive:

    Log monitor found monitored events. Alert Events on server win2k3cdx08.thisismytestdomain 
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: - Target Server Name: localhost Target Server Info: localhost Caller Process ID: 868 Source Network Address: - Source Port: - 
    
    
    
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: - Target Server Name: localhost Target Server Info: localhost Caller Process ID: 868 Source Network Address: - Source Port: - 
    
    
    
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: - Target Server Name: localhost Target Server Info: localhost Caller Process ID: 868 Source Network Address: - Source Port: - 
    
    
    
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: {700c1f89-4b6a-39d8-a2f5-d563542e729c} Target Server Name: localhost Target Server Info: localhost Caller Process ID: 332 Source Network Address: 127.0.0.1 Source Port: 0 
    
    
    
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: - Target Server Name: localhost Target Server Info: localhost Caller Process ID: 868 Source Network Address: - Source Port: - 
    
    
    
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: - Target Server Name: localhost Target Server Info: localhost Caller Process ID: 868 Source Network Address: - Source Port: - 
    
    
    
    MachineName	EventID	Message
    WIN2K3CDX08	552	Logon attempt using explicit credentials: Logged on user: User Name: WIN2K3CDX08$ Domain: THISISMYTESTDOM Logon ID: (0x0,0x3E7) Logon GUID: - User whose credentials were used: Target User Name: Administrator Target Domain: THISISMYTESTDOM Target Logon GUID: {00c11455-27ef-4ff5-6174-d3ad079fa1e0} Target Server Name: localhost Target Server Info: localhost Caller Process ID: 332 Source Network Address: 127.0.0.1 Source Port: 0 
    
    Monday, March 15, 2010 10:31 PM
  • What are you using for a mail client?  The ones I get look quite different.  It's send them as HTML.
    Tuesday, March 16, 2010 1:15 AM
  • I just send via my local ISP and am getting them in Outlook. 
    Tuesday, March 16, 2010 2:42 AM
  • Doesn't look like HTML.  If you've got a HotMail or Gmail account, try sending them there and see how they look, or if you ISP has a webmail interface into your mailbox, try looking at them from there.

    Tuesday, March 16, 2010 3:41 AM
  • It must be the way I am sending it.  It looks the same in Hotmail and my Webmail.  I'll have to figure out how to change the way it's sending through my ISP.
    Tuesday, March 16, 2010 3:56 AM
  • I'll post what I'm getting tomorrow.
    Tuesday, March 16, 2010 3:58 AM
  • Any luck?
    Wednesday, March 17, 2010 7:05 PM
  • This script looks exactly like what I am looking for but I think I am doing something wrong maybe with my .csv file because it never finds the events i'm looking for. I want to scan system log for source disk event id 7. when I run get content \\My CSV file it returns disk,7. I changed $log = system. Any idea what else I am missing? I am running this against some windows xp desktops maybe that has something to do with it? This is what I get the processing x events is what ever I set it to

    Started processing machinename

    Processing 1000 events.

    Found 0 alert events in 23.030816 seconds.

     

     

     

     

     

    • Edited by Ray Shadow Wednesday, March 17, 2010 7:53 PM
    Wednesday, March 17, 2010 7:47 PM
  • Sorry I got busy and forgot to do that.  I'll go dig one out and post it.

    Wednesday, March 17, 2010 7:51 PM
  • Can you post one of the events?

    It might be better to take this discussion back to the script center.
    Wednesday, March 17, 2010 7:52 PM

  • Here's a sample of what I get monitoring for some call transfer events on my Exchange UM server.  Your email should look like this.


    Log monitor found monitored events. Alert Events on server xxxxs-s2

    MachineName

    EventID

    Message

    XXXXs-S2

    1136

    An error occurred while transferring the call to the phone number "6208". The call ID is: 1751600224-64382209@10.246.20.1.9




    MachineName

    EventID

    Message

    XXXXs-S2

    1136

    An error occurred while transferring the call to the phone number "1066". The call ID is: 2887557520-64376852@10.246.20.1.9

    Wednesday, March 17, 2010 7:55 PM
  • Your CSV file needs to have column headings of Source and ID.

    It should look like:

    "Source","ID"
    "Disk","7"
    Wednesday, March 17, 2010 8:07 PM
  • Your CSV file needs to have column headings of Source and ID.

    It should look like:

    "Source","ID"
    "Disk","7"

    Do the quotes matter?

    Source,ID

    Disk,7

    Thanks for your help
    Ray

    Wednesday, March 17, 2010 8:57 PM
  • No. 

    I'm running it right now with one that looks like this:

    Source,ID
    MSExchange Unified Messaging,2147746928
    MSExchange Unified Messaging,2147746879
    Wednesday, March 17, 2010 9:02 PM
  • Interesting change (at least to me) If I change from the system log to the security log and add a second row to my .csv file (Security,538) I get results 
    Wednesday, March 17, 2010 9:34 PM
  • What do you get if you just run a get-winevent from the console for those events?

    Get-EventLog -ComputerName <computername> -LogName "SYSTEM" -Newest 100 |
    ? {$_.source -eq "disk" -and "$_.instanceid -eq "7"}

    If there's any events with a source of "disk" and an event id of "7" in the first 100 events, that should find them.
    Wednesday, March 17, 2010 9:46 PM
  • What do you get if you just run a get-winevent from the console for those events?

    Get-EventLog -ComputerName <computername> -LogName "SYSTEM" -Newest 100 |
    ? {$_.source -eq "disk" -and "$_.instanceid -eq "7"}

    If there's any events with a source of "disk" and an event id of "7" in the first 100 events, that should find them.
    That works after I deleted a Quote before 7, I am on the road tomorrow so won't get to do more testing until late tomorow, thanks for your help.

    Get-EventLog

     

     

     

    $_.source -eq "disk" -and "$_.instanceid -eq 7"}

    Returns

    Index Time EntryType Source InstanceID Message

    ----- ---- --------- ------ ---------- -------

    3947 Mar 15 17:40 Error Disk 3221487623 The device, \Device\Harddisk0\D, ...

    3946 Mar 15 17:40 Error Disk 3221487623 The device, \Device\Harddisk0\D, ...

    3945 Mar 15 17:40 Error Disk 3221487623 The device, \Device\Harddisk0\D, ...

    3944 Mar 15 17:40 Error Disk 3221487623 The device, \Device\Harddisk0\D, ...

    -ComputerName bnewc16957 -LogName "SYSTEM" -Newest 100 |

    ? {

     

    Wednesday, March 17, 2010 10:02 PM
  • Well, there was a typo in what I posted.  There shouldn't have been a " before $._instanceid.

    Having that quote there, and removing the one before the 7 is  bascically looking for all events with a source of "disk", but if those are the events you're wanting to alert on then your CSV file needs to look like this:

    Source,ID
    disk,3221487623

    • Marked as answer by IamMred Thursday, March 25, 2010 2:45 AM
    Wednesday, March 17, 2010 10:13 PM
  • Hmm... maybe I will try to figure out why mine is so oddly formatted.
    Friday, March 19, 2010 1:54 AM
  • I'd thought about adding an option to inculde the full event text as an attachment, and a short message option for sending text pager or text message alerts.  I might have to pursue that.
    Friday, March 19, 2010 10:49 AM
  • Well, there was a typo in what I posted.  There shouldn't have been a " before $._instanceid.

    Having that quote there, and removing the one before the 7 is  bascically looking for all events with a source of "disk", but if those are the events you're wanting to alert on then your CSV file needs to look like this:

    Source,ID
    disk,3221487623


    That worked after I changed the error code I get the belo, Now I just have to figure out the email side or just write it to a file

    "Started processing bnewc16957

    Processing 500 events.

    Found 28 alert events in 10.6820706 seconds.

    ------------------------------------------------------------

     

    Exception calling "Send" with "1" argument(s): "Failure sending mail."

    At :line:44 char:16

    + $SMTPClient.Send <<<< ($mailmessage)"

    Monday, March 22, 2010 8:40 PM
  • What are you using for an smtp server?  If you don't have access to a mail relay, you won't be able to send email. 

    It wouldn't be hard to modify it to write to a log file, or you could use start-transcript, and run it with the -noemail and -showevents switches and capture what would be written to the console to a file.

    • Marked as answer by IamMred Thursday, March 25, 2010 2:44 AM
    Monday, March 22, 2010 8:51 PM
  • They may have locked down our smtp server so that it does not relay.  A log file would actually be all I need I will start down that path first.  That should provide a good excuse to learn more powershell at least.
    Monday, March 22, 2010 9:11 PM
  • This worked fine on my VMWare setup. However, it is not happy on my test network. I have a domain and workstations. When I add the local server name to the txt file, it works fine. If I try to add the computer name of any other system, I get this error:

    Get-EventLog : Attempted to perform an unauthorized operation.

    I am sure it is something simple and quick, but I can't seem to figure it out. Any ideas?
    Tuesday, March 30, 2010 10:36 PM
  • Are you running it with an account that has permissions to the security event logs on those systems? 

     

    Tuesday, March 30, 2010 10:47 PM
  • I am running it with a domain admin account.  Maybe there is just a permission I am missing somewhere.  :(
    Wednesday, March 31, 2010 12:13 AM
  • Can you get event logs from those servers using

    get-event -computername <computer> -logname Security -newest 100

    from a Powershell console window on the machine you're running the script from?

    I'm running in a windows domain, using a domain admin account and can get events from both member servers and domain controllers remotely.

     

    Wednesday, March 31, 2010 12:27 AM
  • If you're logging failure events, do the logs on the remote machine record logon failures that correspond with the attempts to retrieve the logs?
    Wednesday, March 31, 2010 12:28 AM
  • No.  I am now able to connect to both the log server itself and domain controller, but not the two workstations.  Still getting the Unauthorized Operation error.
    Wednesday, March 31, 2010 6:44 PM
  • I have a question about the following line:

    $log_hits = Get-EventLog -ComputerName $_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.instanceid]}

    Can't one just use .eventid instead like so?

    $log_hits = Get-EventLog -ComputerName$_ -LogName $log -Newest $n |
    ? {$event_list[$_.source + "#" + $_.eventid ]}

    My rational is that instanceid is not shown in event log and event IDs are a more common reference when reviewing logs.

     

    I also added "-Fragment" to the ConvertTo-Html cmdlet, I noticed alot of hidden header html that was not needed in the e-mail, this can help reduce the size of the e-mail (not that it was that big).

    Tuesday, April 13, 2010 3:57 PM
  • When I wrote it, I checked the properties of various logs on my machine (Windows 7) and noted that events in some of the new log types don't have an eventid property.  They all appeared to have an instanceid property that is unique and consistent for each event type, so I used that. 

    Tuesday, April 13, 2010 4:21 PM
  • When I wrote it, I checked the properties of various logs on my machine (Windows 7) and noted that events in some of the new log types don't have an eventid property.  They all appeared to have an instanceid property that is unique and consistent for each event type, so I used that. 


    Thanks I was just wondering. Thanks for the script example (and fast response) I am monitoring windows 2003 systems and looking at the event ID mostly.

    This script has been very helpful in getting me started in scripting repetative SA duties with powershell.

    Tuesday, April 13, 2010 5:14 PM
  • Michelle,

     

    would you mind forwarding your list of events you're monitoring? I've come up with a few critical ones but it's always nice to get input from others.

     

    Thanks,


    Nathan Shelby MCITP:EA, MSCTS: Windows 7, 2003 MCSA, A+, Server+ ntshelby[atsymbolgoeshere]gmail[oneofthemperiodthings]com
    Thursday, April 29, 2010 4:08 PM
  • I believe the list is back up toward the top of the thread.
    Thursday, April 29, 2010 4:26 PM
  • It is, i didn't realize your list was the same as hers in a different format :)
    Nathan Shelby MCITP:EA, MSCTS: Windows 7, 2003 MCSA, A+, Server+ ntshelby[atsymbolgoeshere]gmail[oneofthemperiodthings]com
    Thursday, April 29, 2010 6:52 PM
  • Sorry to ressurect a dead thread, but I'm loving this script and what I can do with it.  The problem is I'm having the same issue as mck74 in that any server I add to the computer list works fine, but I can't get any workstations to work. 

    I get one of two errors when I do try, either "No matches found" or "The network path was not found."

    That seems to me to simply be the names are typed in wrong, but I've rewritten them multiple times and used multiple different workstations.  So I'm wondering if there was ever a solution found to this issue or if it was at least continued in discussion somewhere else (I could find it if it was).  Thanks for the help.

    Monday, August 23, 2010 10:40 PM
  • Are you able to retrieve events from the event logs from the PS console?

    e.g.

    get-eventlog -logname application -computername <computername> -newest 10

     


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
    Monday, August 23, 2010 11:25 PM
  • Yes, that seems to work fine.  Tried it on a few workstations and it worked.
    Tuesday, August 24, 2010 3:37 PM
  • But running it in the script from the same machine and the same user account fails?


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
    Tuesday, August 24, 2010 3:46 PM
  • Yep, it gives me a no matches found error (ignore what I said about "the network path was not found", I think that is related to something else).  Here is a copy of what I got:

     

    Started processing workstationX
    Get-EventLog : No matches found
    At C:\Event Log Monitoring\Error Log Script.ps1:57 char:23
    + $index = (Get-EventLog <<<<  -ComputerName $_ -LogName $log -newest 1).index
        + CategoryInfo          : ObjectNotFound: (:) [Get-EventLog], ArgumentException
        + FullyQualifiedErrorId : GetEventLogNoEntriesFound,Microsoft.PowerShell.Commands.GetEventLogCommand
     
    Processing 200 events.
    Get-EventLog : No matches found
    At C:\Event Log Monitoring\Error Log Script.ps1:75 char:25
    + $log_hits = Get-EventLog <<<<  -ComputerName $_ -LogName $log -Newest $n |
        + CategoryInfo          : ObjectNotFound: (:) [Get-EventLog], ArgumentException
        + FullyQualifiedErrorId : GetEventLogNoEntriesFound,Microsoft.PowerShell.Commands.GetEventLogCommand
     
    Found 0 alert events in 0.3792096 seconds.

    Tuesday, August 24, 2010 3:56 PM
  • What log are you trying to get events from?
    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
    Tuesday, August 24, 2010 4:06 PM
  • A few application and a few system but mostly security.

    . . . ok I was about to say I tried all three of those in the console but apparently I didn't try security (could of swore I did, my bad).  It will give me the same error when I try to check the security log in the console.

    Checked one of the workstations I've been trying, the security log appears to be empty.  Not sure if that's just because it hasn't had any events or because it doesn't get populated.  Either way, that's probably information that can be worked with.

    Tuesday, August 24, 2010 4:26 PM
  • I didn't make any allowances in the script for empty logs. 


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
    Tuesday, August 24, 2010 4:38 PM
  • Would it be a reasonably easy thing for me to figure out how to add to the script?  I'm working on learning what I can but I figure if its going to take me forever to figure that out then I'll just not worry about it for now and try to find a different solution.  Thanks for the help, at least I know why it doesn't work and where to start.

     

    edit: Also just wondering if ps scripts will work directly in the startup script section of a GPO or if I need to use a vb script and have it start up the ps script.  Thanks!

    Tuesday, August 24, 2010 5:27 PM
  • Ok I tried testing it with no security log checks but it still gives me the error, not sure why.
    Tuesday, August 24, 2010 6:45 PM
  • If you don't get it figure out by this evening, email me offline and we'll see if we can get it sorted out.
    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "
    Tuesday, August 24, 2010 8:01 PM
  • Hi,

    This question is already marked as answered. If you still need help, please start a new question.


    -- Bill Stewart [Bill_Stewart]

    Saturday, March 8, 2014 1:41 PM
  • I'll just delete my question and try to figure it out on my own. No sense in creating a new thread for an existing thread. Thanks.
    Monday, March 10, 2014 9:13 PM
  • Another one adding to an old archive -- I just found this and it should answer some questions I have about parsing security logs but I too can't get any hits on events I'm trying to check.  I want source Security, ID 529 and 539.  So I've tried with the alert_events.csv file looking like:

    Source,ID

    Security,529

    Security,539

    I have a mix of Windows 2008 and 2012 servers.  Is anyone else still having this issue?  As a test I added an event ID that I know is already in the security log but get this output:

    Started processing xxmaskedservernamexx

    Processing 200 events.

    Found 0 alert events in 0.2416532 seconds.

    ---------------------------------------------------

    Any suggestions?  Thank you.

    
    Thursday, December 3, 2015 1:20 AM