Checking the Release of Quarantined Messages

Report Who Released a Quarantined Message

The question was asked on Twitter about whether it is possible to notify end users when administrators release outbound messages from the quarantine. Most of the time, email ends up in quarantine when Exchange Online Protection decides that inbound messages contain spam or malware, but it’s possible to direct outbound email to quarantine using mail flow rules or actions invoked by Microsoft Purview DLP policies. Exchange Online can certainly quarantine problematic messages but as far as end users are concerned, outbound messages intercepted in this way go into a black hole.

Some good suggestions resulted. My initial response was to use the Get-QuarantineMessage cmdlet to periodically check messages in quarantine and detect released items on that basis. Michel de Rooij came up with a better solution to use a mail flow rule to look for the X-MS-TrafficTypeDiagnostic or X-MS-Exchange-Generated-Message-Source email headers to see if they were related to quarantine releases. That’s quite an innovative approach. However, in both cases, the problem exists that you don’t have all the information about a quarantined message following its release.

Check the Audit Log

Which brings me to the unified audit log. Exchange Online generates audit events for most operations, including when an administrator releases a message from quarantine. Administrators can search the unified audit log by running the Search-UnifiedAuditLog cmdlet to look for QuarantineReleaseMessage events. For example:

$StartDate = (Get-Date).AddDays(-90)
$EndDate = (Get-Date).AddDays(1)

[array]$Records = Search-UnifiedAuditLog -StartDate $StartDate -EndDate $EndDate -Formatted -ResultSize 1000 -Operations QuarantineReleaseMessage
If (!($Records)) { Write-Host "No audit records found for quarantine release - exiting" ; break }

This search finds all events logged over the last 90 days when someone released a message from quarantine. The problem is that the information captured in audit log records tells us who released a message but doesn’t tell us anything about the message. For instance, the audit record doesn’t capture the direction of the message (inbound or outbound), the sender, its recipients, and the message subject.

That information is available in the data recorded for quarantined messages. It is therefore possible to capture information about quarantined messages periodically and store the data in a repository that can be checked to retrieve message details. To prove the point, I created a PowerShell list and populated it with details of quarantined messages. Here’s the code I used:

[array]$QM = Get-QuarantineMessage
$QMData = [System.Collections.Generic.List[Object]]::new() 
ForEach ($Item in $QM) {
  $DataLine = [PSCustomObject] @{
   Received     = $Item.ReceivedTime
   MessageId    = $Item.MessageId
   Direction    = $Item.Direction
   Sender       = $Item.SenderAddress
   Recipients   = $Item.RecipientAddress -Join ", "
   Subject      = $Item.Subject
   Type         = $Item.Type
   Expires      = $Item.Expires
   Identity     = $Item.Identity 
   Id           = $Item.Identity.Split("\")[0]}

  $QMData.Add($DataLine)
} # End ForEach Item

Creating a Composite View of Quarantine Message Release

Now that we have data for quarantined messages, let’s use it to create the information needed to communicate with users. This code creates another PowerShell list and then loops through the audit records retrieved earlier. The code checks each audit record against the data for quarantined messages to see if a match exists. If it does, we grab the information about the message and combine it with the information from the audit record to generate a composite view about the release from quarantine.

$QMInfo = [System.Collections.Generic.List[Object]]::new() 
ForEach ($Rec in $Records) {
  $AuditData = $Rec.AuditData | ConvertFrom-Json
  [array]$QMFound = $QMData | Where-Object {$_.Id -eq $AuditData.NetworkMessageId}
  If ($QMFound) {
     ForEach ($Item in $QMFound) {
       $DataLine = [PSCustomObject] @{
         MessageId  = $AuditData.NetworkMessageId
         Received   = $Item.Received
         Sender     = $Item.Sender
         Recipients = $Item.Recipients
         Subject    = $Item.Subject
         Type       = $Item.Type
         Expires    = $Item.Expires
         Releasedby = $AuditData.UserId
         ReleasedAt = $Rec.CreationDate }
       $QMInfo.Add($DataLine)
     } # End ForEach $QMFound
    } # End If
} # End ForEach $Records

Figure 1 shows examples of the composite records generated by the code.

Audit data for messages released from quarantine
Figure 1: Audit data for messages released from quarantine

After generating the composite data, it’s then a matter of deciding how to notify end users.

A Directional Oddity

One oddity I noticed is that PowerShell reported a quarantined message as “Outbound” (going out of the tenant) while the Microsoft 365 Defender admin center was certain that the message was “Inbound” (coming into the tenant). Figure 1 shows what Defender reports.

Details of a quarantined message shown by the Microsoft 365 Defender portal
Figure 2: Details of a quarantined message shown by the Microsoft 365 Defender portal

And here’s what Get-QuarantineMessage reported. The other message properties indicate that the message is definitely inbound, so I have no idea why PowerShell thinks otherwise.

Identity                   : 2a008698-201e-497f-3dee-08dad2e835e2\7129d58d-ca5e-7e32-a4f8-676d082ba9af
ReceivedTime               : 30/11/2022 15:33:20
Organization               : a662313f-14fc-43a2-9a7a-d2e27f4f3478
MessageId                  : <PA4PR06MB7264B28C1D73C9EB547DDC5AB8159@PA4PR06MB7264.eurprd06.prod.outlook.com>
SenderAddress              : missf0rtune@hotmail.co.uk
RecipientAddress           : {tony.redmond@xxx.com}
Subject                    : Document 49KB (tony.redmond@xxx.com)
Size                       : 93651
Type                       : High Confidence Phish
PolicyType                 : HostedContentFilterPolicy
PolicyName                 : Default
TagName                    : AdminOnlyAccessPolicy
PermissionToBlockSender    : True
PermissionToDelete         : True
PermissionToPreview        : True
PermissionToRelease        : True
PermissionToRequestRelease : False
PermissionToViewHeader     : False
PermissionToDownload       : True
Released                   : False
ReleaseStatus              : NOTRELEASED
SystemReleased             : False
RecipientCount             : 1
QuarantineTypes            : HighConfPhish
Expires                    : 15/12/2022 15:33:20
RecipientTag               : {Priority Account}
DeletedForRecipients       : {}
QuarantinedUser            : {}
ReleasedUser               : {}
Reported                   : False
Direction                  : Outbound

Looking Everywhere for Data

Often people become dismayed when they look for information and discover that a source doesn’t deliver all the detail they need. It’s often the case inside Microsoft 365 that you can combine data from different sources to come up with an answer. It would be nice if Microsoft captured all the relevant message for a quarantined message release in the audit records, but at least we can find the data.


Learn more about how the Office 365 applications really work on an ongoing basis by subscribing to the Office 365 for IT Pros eBook. Our monthly updates keep subscribers informed about what’s important across the Office 365 ecosystem.

3 Replies to “Checking the Release of Quarantined Messages”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.