A common issue when deploying Exchange Online Protection (EOP) and Microsoft Defender for Office 365 (MDO) with on-premises Exchange is making Exchange aware of the EOP spam filtering. This is because EOP uses slightly different logic to stamp the spam results etc. into the message. Exchange Server needs to be aware of this so that it can take action upon those settings.
On-Premises Spam Confidence Level
Microsoft has used the concept of the Spam Confidence Level (SCL) all the way back to the Intelligent Message Filter (IMF) for Exchange Server 2003. You can take a look at it’s operations guide if you really want a blast from the past!
If we go back to the future and set the time circuits to 1985; the current Docs article outlines how SCL is used nowadays.
Exchange spam confidence level (SCL) thresholds | Microsoft Docs
Note that the client side aspect of spam filtering has been depreciated:
In November, 2016, Microsoft stopped producing spam definition updates for the SmartScreen filters in Exchange and Outlook. The existing SmartScreen spam definitions were left in place, but their effectiveness will likely degrade over time. For more information, see Deprecating support for SmartScreen in Outlook and Exchange.
You should use the SCLJunkThreshold parameter Set-OrganizationConfig cmdlet to set the SCL Junk Email folder threshold value for all mailboxes in the organization. This is the only SCL threshold that you can configure at the organization level. The SCL value is typically assigned to messages by the Content Filter agent.
SCLJunkThreshold is the SCL value that's used when the junk email rule is enabled in the mailbox, and an SCL Junk Email folder threshold isn't configured on the mailbox.
A message with an SCL value that's greater than this value is moved to the Junk Email folder by the junk email rule. The maximum value is 9, and the default value is 4, which means that messages with an SCL value of 5 or higher are moved to the Junk Email folder, and messages with an SCL value of 4 or lower are delivered to the Inbox.
The example below illustrates the default value for the SCLJunkThreshold:
Get-OrganizationConfig | FL *SCL*
The SCL Junk Email folder threshold is enabled by default, because the junk email rule is enabled by default in all mailboxes. If the junk email rule is disabled in the mailbox, the SCL Junk Email folder threshold (for the organization or the mailbox) is disabled for the mailbox.
Note that the message may not get to on-premises if it is sent to EOP quarantine. This action is part for the standard MDO defaults.
It is possible to disable junk mail configuration for an on-premises mailbox using Set-MailboxJunkEmailConfiguration cmdlet. For example:
Set-MailboxJunkEmailConfiguration "Charles Scott" -Enabled $False
As noted below, this no longer functions for Exchange Online (EXO) mailboxes.
Exchange Online Spam Confidence Level
Things are a little bit different in Exchange Online (EXO).
EOP now uses its own mail flow delivery agent to route messages to the Junk Email folder instead of using the junk email rule. The Enabled parameter on the Set-MailboxJunkEmailConfiguration cmdlet no longer has any effect on mail flow. EOP routes messages based on the actions set in anti-spam policies. The user's Safe Sender list and Blocked Senders list will continue to work as usual.
Create Required On-Premises Exchange Transport Rules
In the image below you can see that there are no Exchange transport rules present.
We will use the New-TransportRule cmdlet as the rules can be easily created. Note that the commands have been altered slightly. See the end of this post for more on that.
Three rules are required so that we look for the respective values that EOP will set on the messages it has flagged as spam. For the details on each of these message headers please see X-Forefront-Antispam-Report message header fields.
New-TransportRule -Name "EOP SFV:SPM to SCL 6" -HeaderContainsMessageHeader "X-Forefront-Antispam-Report" -HeaderContainsWords "SFV:SPM" -SetSCL 6 -SetAuditSeverity Low
New-TransportRule -Name "EOP SFV:SKS to SCL 6" -HeaderContainsMessageHeader "X-Forefront-Antispam-Report" -HeaderContainsWords "SFV:SKS" -SetSCL 6 -SetAuditSeverity Low
New-TransportRule -Name "EOP SFV:SKB to SCL 6" -HeaderContainsMessageHeader "X-Forefront-Antispam-Report" -HeaderContainsWords "SFV:SKB" -SetSCL 6 -SetAuditSeverity Low
The commands were executed in the Wingtiptoys lab environment:
And we can verify using Get-TransportRule:
Testing Mail To On-Premises Mailbox
Now it’s time to test!
In this test we are sending an external email to the tenant, specifically to a mailbox that is on-premises. We can use the GTUBE test string to force EOP to flag the message as spam.
You can see the GTUBE text string in the test message below.
Note that it was delivered to the junk folder as expected!
If we then look at the message headers using the Message Header Analyzer (mha.azurewebsites.net) what do we see?
The SCL is set to 6 and the EOP verdict that the message is spam has been honoured.
View Transport Rule Action On Message
If we want to go a step further, the on-premises Exchange message tracking log can be used to look at the action taken on the message.
We can use the MessageTrackingLog examples from this post to help us out. Recall that we need to search all transport instances as the message may have been handled through multiple servers, that is why pipeline is there for the Get-TransportService cmdlet. The recipient and subject are also specified to finetune the results.
Get-TransportService | Get-MessageTrackingLog -Recipient local-1@wingtiptoys.ca -MessageSubject "GTUBE Test 16:00"
Great, so that’s all of the message events. Now we can focus on the one that will have applied the transport rule action which is performed by an agent in Exchange. The command is then updated to only show the AGENT EventIDs.
Get-TransportService | Get-MessageTrackingLog -Recipient local-1@wingtiptoys.ca -MessageSubject "GTUBE Test 16:00" -Source AGENT | FL
Exchange Transport Rule Reported As Version 14.0
In the section where we created the transport rules, the default commands were modified. This is due to the rules reporting the incorrect version. Even when created on a fully updated Exchange 2016 server, the rules were reported as version 14.0.0 which is Exchange 2010 RTM. That is not correct…
Recall that version 15.0.0.* is Exchange 2013 and 15.0.1.* is Exchange 2016.
Below is an example of running the command
New-TransportRule -Name "EOP SFV:SPM to SCL 6" -HeaderContainsMessageHeader "X-Forefront-Antispam-Report" -HeaderContainsWords "SFV:SPM" -SetSCL 6
This appears to be because the AuditSeverity is not set. If you set that value to one of the defined audit levels or alternatively tell the rule not to audit at all then the version number changes to the expected value.
Compare that to the results when the below command is used.
New-TransportRule -Name "EOP SFV:SPM to SCL 6" -HeaderContainsMessageHeader "X-Forefront-Antispam-Report" -HeaderContainsWords "SFV:SPM" -SetSCL 6 -SetAuditSeverity Low
Bonus Fun Fact
Exchange 2010 stored transport rules in a different AD location than Exchange 2013. When the first Exchange 2013 or 2016 server was deployed there was a one time copy operation that duplicated the rules into the new storage location.
Exchange 2010 can't process rules that have the Version or RuleVersion value 15.n.n.n. To be sure all your rules can be processed, only use rules that have the value 14.n.n.n.
This is an upgrade issue that was discussed in the Exchange 2010 decommission blog post.
Cheers,
Rhoderick