Get-winevent script assistance

first time poster here, hoping i am doing this correctly! I am using the script below to send email alerts when there are more than 200 of event 6273 is logged under the security log within a 10 minute period. This script emails the most recent 100 events and works perfectly but includes much more information than we care to see as shown in the output below the code. We are basically just trying to get an email that allows us to quickly skim through the users from the latest 100 events to ensure they are not legitimate ad accounts and if they are, easily determine the offenders ip address and add it to the blocklist. How can i extract and email only the timestamp, accountname, and the calling station identifier formatted with some kind of line break between the entries from this script?

$count = (Get-WinEvent -FilterHashtable @{logname='Security'; Id =6273; StartTime=(Get-Date).AddMinutes(-10)}).count

if ($count -gt 200)
{
    $EventId = 6273

$A = Get-WinEvent -MaxEvents 100  -FilterHashTable @{Logname = "Security" ; ID = $EventId; StartTime=(Get-Date).AddMinutes(-10)} -ErrorAction SilentlyContinue
$Message = $A.message


$EventID = $A.Id
$MachineName = $A.MachineName
$Source = $A.ProviderName


$EmailFrom = "sender@address.com"
$EmailTo = "recipient@address.com"
$Subject ="Password guessing alert"
$Body = "`nMessage: $Message"
$SMTPServer = "webmail.mail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("email@address.com", "password");
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)
  
}else 
{
    Write-Host "Under 200 events"
    Exit
}

Here is the output we currently have scheduled to email us each hour:

Message: Network Policy Server denied access to a user.

Contact the Network Policy Server administrator for more information.

User:
Security ID:S-1-0-0
Account Name:CASTRO
Account Domain:DOMAIN
Fully Qualified Account Name:DOMAIN\CASTRO

Client Machine:
Security ID:S-1-0-0
Account Name:-
Fully Qualified Account Name:-
Called Station Identifier:10.44.17.98
Calling Station Identifier:62.122.184.12

NAS:
NAS IPv4 Address:10.25.254.1
NAS IPv6 Address:-
NAS Identifier:-
NAS Port-Type:Virtual
NAS Port:2678272000

RADIUS Client:
Client Friendly Name:FTD
Client IP Address:10.25.254.1

Authentication Details:
Connection Request Policy Name:FTD-Authentication
Network Policy Name:-
Authentication Provider:Windows
Authentication Server:DC302.domain.com
Authentication Type:PAP
EAP Type:-
Account Session Identifier:-
Logging Results:Accounting information was written to the local log file.
Reason Code:16
Reason:Authentication failed due to a user credentials mismatch. Either the user name provided does not map to an existing user account or the password was incorrect.
Network Policy Server denied access to a user.

Contact the Network Policy Server administrator for more information.

User:
Security ID:S-1-0-0
Account Name:rlinda
Account Domain:DOMAIN
Fully Qualified Account Name:DOMAIN\rlinda

Client Machine:
Security ID:S-1-0-0
Account Name:-
Fully Qualified Account Name:-
Called Station Identifier:10.44.17.98
Calling Station Identifier:83.97.73.104

NAS:
NAS IPv4 Address:10.25.254.1
NAS IPv6 Address:-
NAS Identifier:-
NAS Port-Type:Virtual
NAS Port:2678267904

RADIUS Client:
Client Friendly Name:FTD
Client IP Address:10.25.254.1

Authentication Details:
Connection Request Policy Name:FTD-Authentication
Network Policy Name:-
Authentication Provider:Windows
Authentication Server:DC302.domain.com
Authentication Type:PAP
EAP Type:-
Account Session Identifier:-
Logging Results:Accounting information was written to the local log file.
Reason Code:16
Reason:Authentication failed due to a user credentials mismatch. Either the user name provided does not map to an existing user account or the password was incorrect.

What I do for a couple of scripts that pull events using Get-WinEvent is convert them to XML so I can then pull specific details out of the logs.

$EventXML = [xml]$Event.ToXml()

I don’t remember why, but you need to strongly type the object with [xml]. Then most of the info you want will likely be here

$EventXML.event.eventData.data.'#text'

I quickly tested on an NPS server to make sure the same method works:

$EventId = 6273
$A = Get-WinEvent -MaxEvents 100  -FilterHashTable @{Logname = "Security" ; ID = $EventId} -ErrorAction SilentlyContinue
Foreach ($Event in $A) {
    $EventXML = [xml]$A[0].ToXml()
    [PSCustomObject]@{
        Timestamp = (Get-Date $eventxml.event.system.TimeCreated.SystemTime)
        Username = $EventXml.event.eventData.data.'#text'[1]
        CallingStationID = $EventXml.event.eventData.data.'#text'[8]
    }
}

That’s one idea you’re welcome to try out.

Thank you sooo much, i can see the output on the screen containing just those 3 values!!! Would you be willing to advise how i can insert this into the body of an email? i tried adding $message before the pscustomobject line (as shown below) and it did work but only sent values from 1 event rather than the most recent 100 events and had it formatted all bunched together as shown at the bottom.
I am willing to bet it is something super easy that i am just missing but am also still learning as i go!

$A = Get-WinEvent -MaxEvents 100  -FilterHashTable @{Logname = "Security" ; ID = $EventId} -ErrorAction SilentlyContinue
Foreach ($Event in $A) {
    $EventXML = [xml]$A[0].ToXml()
   $message = [PSCustomObject]@{
        Timestamp = (Get-Date $eventxml.event.system.TimeCreated.SystemTime)
        Username = $EventXml.event.eventData.data.'#text'[1]
        CallingStationID = $EventXml.event.eventData.data.'#text'[8]
    }
}


$EmailFrom = "sender@address.com"
$EmailTo = "recipient@address.com"
$Subject ="Password guessing alert"
$Body = "`nMessage: $Message"
$SMTPServer = "webmail.mail.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $true
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("email@address.com", "password");
$SMTPClient.Send($EmailFrom, $EmailTo, $Subject, $Body)

email output example:


Message: @{Timestamp=10/08/2024 14:24:01; Username=sherif; CallingStationID=136.144.19.187}

in my limited experience with sending emails that contain data, you’re likely going to end up needing to format the message body as HTML in order to get the formatting you want.
Trying to get Powershell object output to look the way you want in a message body can be messy.
You may consider temporarily writing your objects to a CSV file with Export-CSV and then attaching them to the email with the Attachments argument. At least when using Send-MailMessage.

I completely agree with this. You can simply click the attachment and view the data line by line but you also still have structured data you can easily query, build reports/graphs, etc.

thank you both for pointing me in the right direction, i was able to get it to email a csv attachment with the data in 3 separate columns which is really going to make this much easier to skim through! for some reason, the csv only contains data from a single event rather than the last 100 events. Below is the complete code i am now using, am i missing something in one of the lines that is telling it to export only 1 event into the csv as opposed to dumping all 100 of the latest events?

$count = (Get-WinEvent -FilterHashtable @{logname='Security'; Id =6273; StartTime=(Get-Date).AddMinutes(-10)}).count

if ($count -gt 200)
{
    $EventId = 6273


$A = Get-WinEvent -MaxEvents 100  -FilterHashTable @{Logname = "Security" ; ID = $EventId} -ErrorAction SilentlyContinue 
Foreach ($Event in $A) {
    $EventXML = [xml]$A[0].ToXml() 
   $message = [PSCustomObject]@{
        Timestamp = (Get-Date $eventxml.event.system.TimeCreated.SystemTime)
        Username = $EventXml.event.eventData.data.'#text'[1]
        CallingStationID = $EventXml.event.eventData.data.'#text'[8]
    }
} $message  | Export-Csv -Path C:\temp\security.csv


$sendMailMessageSplat = @{
    From = 'NPS Alert <nps.alert@email.com>'
    To = 'User1 <user1@email.com>'
    Subject = 'vpn alert'
    Attachments = 'C:\temp\security.csv'
    DeliveryNotificationOption = 'OnSuccess', 'OnFailure'
    SmtpServer = 'smtp.email.com'
}
Send-MailMessage @sendMailMessageSplat
  
}else 
{
    Write-Host "Under 200 events"
    Exit
}

You are capturing the output inside the loop with the $message variable. Put that BEFORE the loop and you should be good.

$count = (Get-WinEvent -FilterHashtable @{logname='Security'; Id =6273; StartTime=(Get-Date).AddMinutes(-10)}).count

if ($count -gt 200){
    $EventId = 6273


$A = Get-WinEvent -MaxEvents 100  -FilterHashTable @{Logname = "Security" ; ID = $EventId} -ErrorAction SilentlyContinue 

$message = foreach ($Event in $A) {
    $EventXML = [xml]$A[0].ToXml() 
    [PSCustomObject]@{
        Timestamp = (Get-Date $eventxml.event.system.TimeCreated.SystemTime)
        Username = $EventXml.event.eventData.data.'#text'[1]
        CallingStationID = $EventXml.event.eventData.data.'#text'[8]
    }
}

$message  | Export-Csv -Path C:\temp\security.csv

$sendMailMessageSplat = @{
    From = 'NPS Alert <nps.alert@email.com>'
    To = 'User1 <user1@email.com>'
    Subject = 'vpn alert'
    Attachments = 'C:\temp\security.csv'
    DeliveryNotificationOption = 'OnSuccess', 'OnFailure'
    SmtpServer = 'smtp.email.com'
}
Send-MailMessage @sendMailMessageSplat
  
} else {
    Write-Host "Under 200 events"
    Exit
}
1 Like

Well, i won’t be making that mistake again! going to call this a learning experience! thank you both sooo much for your help with this, have a great day!

1 Like

I assure you, we’ve all made that mistake before.

so i just expanded the column with the date stamp and just happened to notice that this is sending a single event duplicated 100 times! is there a specific line in the script that i should be researching that i can update that would send me the latest unique 100 events?

oh, i see it

$message = foreach ($Event in $A) {
    $EventXML = [xml]$A[0].ToXml() 

Change the $A[0] to $Event.
This “loop” has technically been just looping through the number of messages retrieved, but only ever taking action against the first one. that’s my bad because the code I posted was written that way. Apologies as that’s an obvious oversight on my part from when I was testing stuff.

i can see i have lots to learn here, that would have taken me at least another full day of researching to figure that out and my eyes are already going cross eyed staring at this screen all day, thanks again!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.