In today's interconnected business environment, email remains a critical communication channel for many organizations. When important messages fail to arrive, it can disrupt operations, delay critical processes, and potentially impact customer service. This is especially true for automated system notifications, inter-departmental communications, and messages from external partners.
In this blog post, I'll share a PowerShell solution that monitors email communications between specific addresses and sends alerts when expected messages aren't received. Whether you're monitoring incoming messages from a business partner or ensuring outgoing notifications are properly sent, this script provides peace of mind through automated verification.
The Problem: Missing Critical Emails
Has your organization ever faced these scenarios?
- A vital daily report email from a business partner never arrived, but nobody noticed until it was too late
- Your automated notification system failed to send confirmation emails to customers
Traditional monitoring solutions often overlook email flows, focusing instead on server health or general mail flow. Our solution addresses this gap by monitoring specific sender-recipient pairs and alerting you when expected communications don't arrive within a designated timeframe.
Email Monitoring Script
This script leverages Exchange Online's message tracking capabilities to search for emails between specific addresses within a defined time window. If no matching emails are found, it sends a professionally formatted alert email to your designated monitoring address.
Key Features
- Directional Monitoring: Monitor both inbound and outbound email flows
- Configurable Check Intervals: Define how frequently to check for messages
- Professional Alert Emails: Sends eye-catching HTML emails with clear warning indicators
- Console Visibility: Real-time status updates in the PowerShell console
- Detailed Logging: All activities recorded to a log file for audit purposes
Prerequisites
- PowerShell 5.1 or later
- Exchange Online PowerShell module installed (
Install-Module ExchangeOnlineManagement
) - Appropriate Exchange Online permissions to run message tracking queries
- Access to an SMTP server for sending alert emails
Understanding the Script Components
The script consists of several core components:
- Configuration Parameters: Define monitoring behavior and connection details
- Exchange Online Connection: Establish access to message tracking logs
- Email Checking Logic: Search for messages within the specified timeframe
- Alert Generation: Create and send professional HTML notification emails
- Monitoring Loop: Continuously check for messages on the defined schedule
Let's examine each component in detail.
Configuration Parameters
The script accepts several parameters to customize its behavior:
param (
[Parameter(Mandatory=$false)]
[string]$LogPath = "C:\Logs\EmailMonitoring.log",
[Parameter(Mandatory=$false)]
[int]$CheckIntervalMinutes = 5,
[Parameter(Mandatory=$false)]
[ValidateSet("Inbound", "Outbound")]
[string]$Direction = "Inbound",
[Parameter(Mandatory=$false)]
[string]$AlertEmail = "alerts@company.com",
[Parameter(Mandatory=$true)]
[string]$SMTPServer = "smtp.company.com"
)
# Predefined sender and receiver addresses
$SenderEmail = "sender@center.com"
$ReceiverEmail = "receiver@company.com"
$MailboxEmail = "monitoring@company.com"
These parameters and variables control:
- Where logs are stored
- How often to check for messages (default: every 5 minutes)
- Which direction to monitor (inbound or outbound)
- Where to send alerts when messages are missing
- Which SMTP server to use for sending alerts
- The specific email addresses to monitor
Exchange Online Connection
The script connects to Exchange Online using modern authentication:
try {
Write-Host "Attempting to connect to Exchange Online using modern authentication..."
-ForegroundColor Cyan
Write-Log "Attempting to connect to Exchange Online using modern authentication..."
# Import the Exchange Online Management module
Import-Module ExchangeOnlineManagement -ErrorAction Stop
# Connect using modern authentication
Connect-ExchangeOnline -ShowBanner:$false
Write-Host "Successfully connected to Exchange Online" -ForegroundColor Green
}
catch {
$errorMessage = "Failed to connect to Exchange Online: $_"
Write-Host $errorMessage -ForegroundColor Red
Write-Log $errorMessage
exit 1
}
This establishes the connection needed to query message tracking logs in Office 365.
Email Checking Logic
The heart of the solution is the email checking function, which searches message traces for communications between the specified addresses:
function Check-Emails {
try {
# Calculate time threshold (current time minus interval)
$timeThreshold = (Get-Date).AddMinutes(-$CheckIntervalMinutes)
# Search for emails based on direction
if ($Direction -eq "Inbound") {
# Checking inbound: emails FROM sender TO receiver
$recentEmails = Get-MessageTrace -SenderAddress $SenderEmail
-RecipientAddress $ReceiverEmail -StartDate $timeThreshold
-EndDate (Get-Date)
} else {
# Checking outbound: emails FROM receiver TO sender
$recentEmails = Get-MessageTrace -SenderAddress $ReceiverEmail
-RecipientAddress $SenderEmail -StartDate $timeThreshold -EndDate (Get-Date)
}
$emailCount = ($recentEmails | Measure-Object).Count
# Return true if emails were found, false otherwise
return ($emailCount -gt 0)
}
catch {
$errorMessage = "Error checking emails: $_"
Write-Host $errorMessage -ForegroundColor Red
Write-Log $errorMessage
return $false
}
}
This function:
- Calculates the time window to check (last X minutes)
- Queries Exchange Online message tracking based on the direction
- Returns a boolean indicating whether any matching emails were found
Alert Email Generation
When expected emails aren't found, the script sends a professional HTML alert:
function Send-AlertEmail {
param (
[string]$Subject,
[string]$BodyText
)
try {
# Create professional HTML email body with warning elements
$htmlBody = @"
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 0; }
.banner { background-color: #FF0000; color: white; padding: 15px;
text-align: center; font-weight: bold; font-size: 18px; }
.warning-icon { font-size: 36px; margin-right: 10px; vertical-align: middle; }
.content { padding: 20px; }
.footer { font-size: 12px; color: #666; padding: 10px; border-top: 1px solid #eee; }
.highlight { background-color: #FFF0F0; padding: 15px; border-left: 5px solid
#FF0000; margin: 10px 0; }
</style>
</head>
<body>
<div class="banner">
<span class="warning-icon">⚠️</span> ALERT: EMAIL MONITORING SYSTEM
</div>
<div class="content">
<h2>Email Communication Failure Detected</h2>
<div class="highlight">
<p><strong>$BodyText</strong></p>
<p>Please investigate this issue immediately as it may indicate
a communication breakdown.</p>
</div>
<p>Details:</p>
<ul>
<li><strong>Time of Alert:</strong> $(Get-Date
-Format 'yyyy-MM-dd HH:mm:ss')</li>
<li><strong>Direction:</strong> $Direction</li>
<li><strong>Monitoring Period:</strong> Last $CheckIntervalMinutes minutes</li>
</ul>
<p>This is an automated alert from the Email Monitoring System.</p>
</div>
<div class="footer">
<p>IT Operations | Do not reply to this email | Internal use only</p>
</div>
</body>
</html>
"@
# Send email using SMTP server (no authentication)
$emailParams = @{
From = $MailboxEmail
To = $AlertEmail
Subject = $Subject
Body = $htmlBody
BodyAsHtml = $true
SmtpServer = $SMTPServer
Port = 25
}
Send-MailMessage @emailParams
}
catch {
Write-Host "Failed to send alert email: $_" -ForegroundColor Red
Write-Log "Failed to send alert email: $_"
}
}
The alert email includes:
- A bold red header with warning icon
- Clear description of the missing communication
- Important context details (time, direction, monitoring period)
- Professional formatting that draws attention to the issue
Monitoring Loop
Finally, the script runs in a continuous loop, checking for emails at the specified interval:
# Main monitoring loop
while ($true) {
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
Write-Host "`n===== Email Monitoring Check: $timestamp =====" -ForegroundColor Magenta
$emailsReceived = Check-Emails
if (-not $emailsReceived) {
if ($Direction -eq "Inbound") {
$alertSubject = "⚠️ ALERT: Missing Inbound Email from $SenderEmail"
$alertBody = "No inbound emails from $SenderEmail to
$ReceiverEmail have been received in the last $CheckIntervalMinutes minutes."
} else {
$alertSubject = "⚠️ ALERT: Missing Outbound Email to $SenderEmail"
$alertBody = "No outbound emails from $ReceiverEmail to
$SenderEmail have been sent in the last $CheckIntervalMinutes minutes."
}
Send-AlertEmail $alertSubject $alertBody
} else {
Write-Host "Communication status: OK - No action needed" -ForegroundColor Green
}
# Wait before checking again
$nextCheckTime = (Get-Date).AddMinutes($CheckIntervalMinutes)
.ToString("yyyy-MM-dd HH:mm:ss")
Write-Host "Next check scheduled for: $nextCheckTime" -ForegroundColor Cyan
Start-Sleep -Seconds ($CheckIntervalMinutes * 60)
}
This loop:
- Checks for emails using our defined function
- Sends alerts when expected emails are missing
- Waits for the specified interval before checking again
Running the Script
To use this script in your environment:
- Save the complete script to a file (e.g.,
Monitor-Mailbox.ps1
) - Edit the predefined email addresses to match your monitoring needs
- Run the script with your SMTP server:
.\Monitor-Mailbox.ps1 -SMTPServer "your-smtp-server.com"
You can customize other parameters as needed:
.\Monitor-Mailbox.ps1 -SMTPServer "your-smtp-server.com" -Direction "Outbound"
-CheckIntervalMinutes 10 -AlertEmail "emergency@company.com"
What the Alert Email Looks Like
When an expected email isn't found, the script sends a professionally formatted alert that looks like this:
The clear red banner and highlighted warning text immediately draw attention to the issue, its also designed very differently to other "failure" emails.