An updated and improved version of my old script from here.
This script has been tested against Exchange 2016 CU4. I do not know if it will work against older versions of Exchange.
The script can be configured to run as a scheduled task and it generates a e-mail report of users who have sent more than ‘x’ e-mails so far today or the previous day.
You can now exclude specific e-mails from the report by placing them in the ‘exceptionList.txt’ file which is created after the scripts first run
We use this script to find compromised accounts that are blasting out spam.
# Users who sent more than 'x' e-mails so far today or yesterday # The output of this report is then e-mailed # # This script must be called using the argument '-TimeFrame "yesterday"' or '-TimeFrame "today"' # # On first run a file called 'exceptionList.txt' in the same directory as itself if the file doesn't exist. This means the script will need # read-write access to the location on the first run and read-only access from then on. Once created you can add e-mail addresses into the # text file (one per line) that you want excluded from the report. # # Note: This script has been specifically tested against Exchange 2016 CU4 and may not work against previous versions of Exchange # # Requirements: The account that runs this script needs at least the "View-Only Organization Management" and the "Records Management" role in Exchange # # Created by: Eric Schewe # Created on: 2017-08-15 # # This command must be run with '-timeFrame today' or '-timeFrame yesterday' specified Param( [Parameter(Mandatory=$False)] [string]$timeFrame ) # ------------------------- (START) Customize this section per your deployment ------------------------- # Set the minimum sent e-mails count you care about. Anyone who sends more than this number will appear in the report $minimumEmails = 500 # Location of script, no trailing slash $scriptLocation = "" # E-mail stuff # Multiple e-mail addresses should be in this format "<[email protected]>, <[email protected]>" $to = "" $from = "[email protected]" $smtpServer = "smtp.mydomain.com" # Exchange server you want to run this PowerShell against $exchangePowerShellServer = "exchange01.mydomain.com" # ------------------------- (END) Customize this section per your deployment ------------------------- if ($timeFrame -match "today") { # How we define today $startTime = (get-date -Hour 00 -Minute 00 -Second 00) $endTime = (get-date -Hour 23 -Minute 59 -Second 59) $subject = "Staff who sent over $minimumEmails e-mails for $startTime to now" } elseif ($timeFrame -match "yesterday") { # How we define yesterday $startTime = (get-date -Hour 00 -Minute 00 -Second 00).AddDays(-1) $endTime = (get-date -Hour 23 -Minute 59 -Second 59).AddDays(-1) $subject = "Staff who sent over $minimumEmails e-mails for $startTime to $endTime" } else { # Output parameter information if it isn't specified Write-Host "Please specify '-timeFrame today' or '-timeFrame yesterday' when invoking this script" -ForeGroundColor "Red" Exit } # Debugging - This is useful if you're executiong the script under your account and need to test with different credentials #$UserCredential = Get-Credential #$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://brockton-03.it.int.viu.ca/PowerShell/ -Authentication Kerberos -Credential $UserCredential # Production - This assumes you're running this as a scheduled task under a user account with the proper credentials so we don't prompt for credentials $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri http://$exchangePowerShellServer/PowerShell/ -Authentication Kerberos # Assuming we've gotten here in the script start a Exchange session Import-PSSession $Session -AllowClobber # Check if the exception list exists, create it if it doesn't and then read the exception list into the script skipping blank lines and comments if (!(Test-Path "$($scriptLocation)\exceptionList.txt")) { New-Item -path $($scriptLocation) -name exceptionList.txt -type "file" -value "# One entry per line, E-mail addresses are NOT case sensitive" | Out-Null } $exceptionList = Get-Content "$($scriptLocation)\exceptionList.txt" | Where-Object { $_ -notmatch "#" -or $_ -notmatch "" } # Get a list of transport servers in case there is more than one $transportServers = Get-TransportService # Initialize the variable so we can append to it. Initial stats array is cast differently because we can't use "Group-Object" on an ArrayList and we can't use .Remove on a Array $mailStats = @() [System.Collections.ArrayList]$mailStatsToDelete = @() [System.Collections.ArrayList]$mailStatsArrayList = @() # Search the message tracking log on each transport server foreach ($server in $transportServers) { # Search the message tracking log within a time frame on each transport server only looking for the 'SENDEXTERNAL' EventID $mailStats += Get-MessageTrackingLog -Server $server.name -Start $startTime -End $endTime -EventID "SENDEXTERNAL" -ResultSize Unlimited } # Filter out anyone that didn't send enough e-mails $mailStats = $mailStats |Group-Object -Property Sender | Where-Object {$_.Group.Recipients.Count -gt $minimumEmails} # Convert the Array to an ArrayList so we can use the .Remove method and so we can sort things later foreach ($stat in $mailStats) { $line = "" | Select-Object Email,Total $line.Email = $stat.Name $line.Total = $stat.Group.Recipients.Count $mailStatsArrayList += $line } # If there are entries in the exception list grab all of the matches and store them in a seperate array if ($exceptionList.Count -ne 0) { # Go through the list of exceptions and find any matches and store them foreach ($exception in $exceptionList) { foreach ($stat in $mailStatsArrayList) { if ($stat.Email -like $exception) { $mailStatsToDelete += $stat } } } # Remove the matched exceptions from the final results foreach ($stat in $mailStatsToDelete) { $mailStatsArrayList.Remove($stat) } } # Check and see if there are any results to report if ($mailStatsArrayList.Count -ne 0) { # Sort and format the output into a table for the e-mail $results = $mailStatsArrayList | Sort-Object -Property Total -Descending | Format-Table -Property @{Expression={$_.Total};Label="Count";Width=15; Alignment="left"},@{Expression={$_.Email};Label="Sender"; Width=250; Alignment="left"} |Out-String -width 300 # Send the e-mail $smtp = new-object Net.Mail.SmtpClient($smtpServer) $smtp.Send($from, $to, $subject, $results) } # Clean-up our session Remove-PSSession $Session
We recently implemented MFA and noticed the script we had working on stopped working.
Any ideas what we need to do to get this working again?
I’d guess the account running the script is now trying to prompt for MFA. I’d try running it on an account with out MFA enabled and see how that goes.