Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
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

Redirecting Unwanted Emails via Transport Rules After Moving to a New Email Platform

After migrating to a new email solution that routes messages into Microsoft 365, we discovered an unexpected side effect: messages from blocked senders and domains were no longer being filtered by Microsoft Defender for Office 365.

Why? Because our new mail gateway was explicitly trusted by Defender. This trust bypassed many of the built-in anti-spam mechanisms, including policies that normally block specific senders or domains.

As a result, messages from known bad actors — senders and domains we had already identified — were now being delivered to user inboxes.

Move Blocking Logic to Transport Rules

To enforce our existing blocks after the new platform hands off the message to Exchange Online, we needed to move the logic into Exchange transport rules.

Transport (mail flow) rules operate after the message has been accepted by Microsoft 365 and are not impacted by trust relationships like those in the anti-spam engine.

Challenge: ~2000 Senders and Domains

Our block lists had grown over time and contained roughly 2,000 entries — a mix of:

  • Specific sender addresses (e.g. mailcious.user@blackhole.local)
  • Entire domains (e.g., blackhole.local)

However, Exchange Online transport rules have size limits. A single rule can only hold a limited number of conditions, so we had to break the block list into smaller, manageable chunks.

Transport rule “states”

I need to point out a peculiar difference of the transport rule state, depending on where it’s created:

  1. Exchange Admin Center (EAC): Transport (mail flow) rules are created in a disabled state 
  2. Powershell (Exchange Online or on-prem): Transport rules are enabled by default when created using New-TransportRule

This means as we are scripting transport rules I would highly recommend you create all your transport rules in the disabled state so when they’re all created, you can review them before you enable them:

-Enabled:$false

Step 1: Split the Sender/Domain List into Manageable Chunks

This requires a script to split the large list into multiple smaller files:

$inputFile = "C:\BlockList\domains.txt"
$outputFolder = "C:\BlockList\Chunks"
$splitCount = 4

$lines = Get-Content $inputFile | Where-Object { $_.Trim() -ne "" }
$perFile = [Math]::Ceiling($lines.Count / $splitCount)

for ($i = 0; $i -lt $splitCount; $i++) {
    $start = $i * $perFile
    $end = [Math]::Min($start + $perFile - 1, $lines.Count - 1)
    $chunk = $lines[$start..$end]
    $chunk | Set-Content "$outputFolder\blocklist_part$($i + 1).txt"
}

Step 2 : Cleanup the Text File

This will remove characters from the file that could cause parsing errors

# Path to the domain list file
$domainListPath = "senders_part1.txt"

# Clean and prepare the domain list
$domains = Get-Content $domainListPath | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() }
    $chunk = $lines[$start..$end]

Step 3: Create Transport Rules to Enforce Blocking

Depending on your organization's policy, you can either:

  • Silently delete the messages (they disappear with no bounce or quarantine)
  • Quarantine the messages (users or admins can review and release them)

Each option uses a separate action in the transport rule.

Option A: Delete Emails Silently

$senders = Get-Content "C:\BlockList\blocklist_part1.txt"
New-TransportRule -Name "Block Senders - Part 1" `
    -From $senders `
    -DeleteMessage $true  
    -Enabled:$false

Or for domains:

$domains = Get-Content "C:\BlockList\blocklist_domains1.txt"
New-TransportRule -Name "Block Domains - Part 1" `
    -SenderDomainIs $domains `
    -DeleteMessage $true
    -Enabled:$false

Option B: Send Messages to Hosted Quarantine

$senders = Get-Content "C:\BlockList\blocklist_part1.txt"
New-TransportRule -Name "Quarantine Senders - Part 1" `
    -From $senders `
    -Quarantine $True 
    -Enabled:$false

For domains:

$domains = Get-Content "C:\BlockList\blocklist_domains1.txt"
New-TransportRule -Name "Quarantine Domains - Part 1" `
    -SenderDomainIs $domains `
    -Quarantine $True 
    -Enabled:$false

By using transport rules at the mail flow level, these blocks take effect regardless of trust status between your perimeter gateway and Microsoft 365. It gives you deterministic, policy-based control over unwanted messages — even when Defender for Office 365 doesn’t intervene.

Script : Command.ps1

If you need this as an example to create the transport rule with the error checking then you can do something like this:

Path to the domain list file
$domainListPath = "senders_part1.txt"

# Clean and prepare the domain list
$domains = Get-Content $domainListPath | Where-Object { $_.Trim() -ne "" } | ForEach-Object { $_.Trim() }

# Transport rule name
$ruleName = "Block Spam Senders #1"

# Check if rule already exists
$existingRule = Get-TransportRule -Identity $ruleName -ErrorAction SilentlyContinue

if ($existingRule) {
    # Update existing rule
    Set-TransportRule -Identity $ruleName -SenderDomainIs $senderDomains -Quarantine $True 
    Write-Host "Transport rule '$ruleName' updated."
} else {
    # Create new rule
    New-TransportRule -Name $ruleName -From $Domains -Quarantine $True -Enabled:$false
    Write-Host "Transport rule '$ruleName' created."
}
Previous Post Next Post

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