Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Exchange Online Domain Compliance: Unauthorised Mailbox Monitoring

It can sometimes be a challenge in maintaining control over email domain usage is crucial for security and brand consistency. I recently developed a PowerShell solution to address a common challenge: monitoring when mailboxes are created using non-standard or unapproved domains in Exchange Online.

The Challenge

Organizations often have multiple accepted domains in Exchange Online - some for primary business use, others for legacy systems, acquisitions, or specific services. However, not all accepted domains should be actively used for new mailboxes. Without proper monitoring, mailboxes can be created with non-standard domains, leading to:

  • Brand inconsistency
  • Security vulnerabilities
  • Compliance issues
  • Email deliverability problems

I needed a solution that would automatically detect when mailboxes were created outside our approved domain list and alert the appropriate teams.

HTML Email Visual

This is an example of the HTML email


The Solution Architecture

The script that connects to Exchange Online and implements an intelligent tracking system. The script maintains state between runs, ensuring we only get notified about genuinely new mailboxes rather than being bombarded with the same information repeatedly.

Core Components

The solution consists of three main components:

  1. Domain Filtering Logic - Identifies which domains to monitor
  2. State Tracking System - JSON-based persistence to track previously discovered mailboxes
  3. Notification System - HTML email reports with CSV attachments

Implementation Deep Dive

Setting Up Domain Exclusions

First, I defined the approved domains that should be excluded from monitoring:

# Define excluded domains
$excludedDomains = @(
    "bearco.onmicrosoft.com",
    "croucher.cloud"
)

These are our standard corporate domains where mailbox creation is expected and approved.

Efficient Domain Querying

Instead of checking each domain individually (which would be slow), I built a single filter query that retrieves all relevant mailboxes in one operation:

# Get all accepted domains and filter out excluded ones
$domainsToCheck = Get-AcceptedDomain | 
    Select-Object -ExpandProperty DomainName | 
    Where-Object { $_ -notin $excludedDomains }

# Build filter for all domains at once
$filter = ($domainsToCheck | ForEach-Object { 
    "EmailAddresses -like '*@$_'" 
}) -join ' -or '

# Get all mailboxes matching any of the non-excluded domains
$mailboxes = Get-Mailbox -ResultSize Unlimited -Filter $filter

This approach significantly improves performance by making a single query to Exchange Online rather than multiple queries per domain.

Implementing State Tracking

The key to making this solution practical was implementing state tracking. I used a JSON file to maintain a record of all previously discovered mailboxes:

# Check if this is first run
$isFirstRun = -not (Test-Path $jsonPath)

# Load previous results
$previousMailboxes = @{}
if (-not $isFirstRun) {
    $jsonContent = Get-Content $jsonPath -Raw | ConvertFrom-Json
    foreach ($prop in $jsonContent.PSObject.Properties) {
        $previousMailboxes[$prop.Name] = $prop.Value
    }
}

This allows the script to differentiate between:

  • First run: Establishes a baseline of all existing mailboxes
  • Subsequent runs: Only reports genuinely new mailboxes

Processing and Categorizing Mailboxes

For each mailbox found, I capture comprehensive information and determine which non-standard domain it's using:

foreach ($mailbox in $mailboxes) {
    # Find which domain this mailbox uses
    $matchedDomain = ""
    foreach ($email in $mailbox.EmailAddresses) {
        foreach ($domain in $domainsToCheck) {
            if ($email -like "*@$domain") {
                $matchedDomain = $domain
                break
            }
        }
        if ($matchedDomain) { break }
    }
    
    $mailboxData = @{
        UserPrincipalName = $mailbox.UserPrincipalName
        DisplayName = $mailbox.DisplayName
        PrimarySmtpAddress = $mailbox.PrimarySmtpAddress
        Domain = $matchedDomain
        MailboxType = $mailbox.RecipientTypeDetails
        WhenCreated = $mailbox.WhenCreated
        FirstSeen = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }
}

Smart Notification Logic

The script only sends notifications when action is needed:

# Save CSV and send email only if there are new mailboxes or it's the first run
if ($isFirstRun -or $newMailboxes.Count -gt 0) {
    # Export to CSV
    if ($isFirstRun) {
        # First run - export all mailboxes as baseline
        $allMailboxes.Values | ForEach-Object { [PSCustomObject]$_ } | 
            Export-Csv -Path $csvPath -NoTypeInformation
    } else {
        # Subsequent runs - export only new
        $newMailboxes | Export-Csv -Path $csvPath -NoTypeInformation
    }
}

This prevents alert fatigue by ensuring emails are only sent when there's something to report.

HTML Email Reports

I implemented HTML-formatted emails that provide a clear, professional summary of findings:

$htmlBody = @"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {
            font-family: Arial, sans-serif;
            color: #333;
            line-height: 1.6;
        }
        .header {
            background-color: #004080;
            color: white;
            padding: 20px;
            border-radius: 5px 5px 0 0;
        }
        .summary {
            background-color: white;
            padding: 15px;
            margin: 15px 0;
            border-radius: 5px;
            border: 1px solid #e0e0e0;
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>Exchange Domain Audit Report</h2>
        <p><strong>New Mailboxes Since Last Run:</strong> 
           <span class='highlight'>$($newMailboxes.Count)</span></p>
    </div>
</body>
</html>
"@

The email includes:

  • A summary of findings
  • Domain statistics showing which domains are being used
  • Professional formatting without unnecessary graphics
  • CSV attachment with detailed mailbox information

Running the Script

The script can be executed manually or scheduled via Task Scheduler:

.\ExchangeDomainAudit.ps1

Output example when new mailboxes are found:

Connecting to Exchange Online...
Getting accepted domains...
Domains to check: pokebearswithsticks.com, bythepowerofgreyskull.com

Searching for mailboxes...
Found 41 mailboxes with non-excluded domains

RESULTS:
Total mailboxes: 41
NEW mailboxes: 3

New mailboxes saved to: NewMailboxes_20251205_140000.csv
Sending email report...
Email sent successfully to: lee@croucher.cloud

Conclusion

This automated monitoring solution provides a practical approach to maintaining domain compliance in Exchange Online. By combining intelligent state tracking with targeted notifications, it delivers exactly the information needed without creating noise.

Previous Post Next Post

نموذج الاتصال