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.
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.
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.
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.
{"id":null,"mode":"button","open_style":"in_modal","currency_code":"EUR","currency_symbol":"\u20ac","currency_type":"decimal","blank_flag_url":"https:\/\/office365itpros.com\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/blank.gif","flag_sprite_url":"https:\/\/office365itpros.com\/wp-content\/plugins\/tip-jar-wp\/\/assets\/images\/flags\/flags.png","default_amount":100,"top_media_type":"featured_image","featured_image_url":"https:\/\/office365itpros.com\/wp-content\/uploads\/2022\/11\/cover-141x200.jpg","featured_embed":"","header_media":null,"file_download_attachment_data":null,"recurring_options_enabled":true,"recurring_options":{"never":{"selected":true,"after_output":"One time only"},"weekly":{"selected":false,"after_output":"Every week"},"monthly":{"selected":false,"after_output":"Every month"},"yearly":{"selected":false,"after_output":"Every year"}},"strings":{"current_user_email":"","current_user_name":"","link_text":"Virtual Tip Jar","complete_payment_button_error_text":"Check info and try again","payment_verb":"Pay","payment_request_label":"Office 365 for IT Pros","form_has_an_error":"Please check and fix the errors above","general_server_error":"Something isn't working right at the moment. Please try again.","form_title":"Office 365 for IT Pros","form_subtitle":null,"currency_search_text":"Country or Currency here","other_payment_option":"Other payment option","manage_payments_button_text":"Manage your payments","thank_you_message":"Thank you for supporting the work of Office 365 for IT Pros!","payment_confirmation_title":"Office 365 for IT Pros","receipt_title":"Your Receipt","print_receipt":"Print Receipt","email_receipt":"Email Receipt","email_receipt_sending":"Sending receipt...","email_receipt_success":"Email receipt successfully sent","email_receipt_failed":"Email receipt failed to send. Please try again.","receipt_payee":"Paid to","receipt_statement_descriptor":"This will show up on your statement as","receipt_date":"Date","receipt_transaction_id":"Transaction ID","receipt_transaction_amount":"Amount","refund_payer":"Refund from","login":"Log in to manage your payments","manage_payments":"Manage Payments","transactions_title":"Your Transactions","transaction_title":"Transaction Receipt","transaction_period":"Plan Period","arrangements_title":"Your Plans","arrangement_title":"Manage Plan","arrangement_details":"Plan Details","arrangement_id_title":"Plan ID","arrangement_payment_method_title":"Payment Method","arrangement_amount_title":"Plan Amount","arrangement_renewal_title":"Next renewal date","arrangement_action_cancel":"Cancel Plan","arrangement_action_cant_cancel":"Cancelling is currently not available.","arrangement_action_cancel_double":"Are you sure you'd like to cancel?","arrangement_cancelling":"Cancelling Plan...","arrangement_cancelled":"Plan Cancelled","arrangement_failed_to_cancel":"Failed to cancel plan","back_to_plans":"\u2190 Back to Plans","update_payment_method_verb":"Update","sca_auth_description":"Your have a pending renewal payment which requires authorization.","sca_auth_verb":"Authorize renewal payment","sca_authing_verb":"Authorizing payment","sca_authed_verb":"Payment successfully authorized!","sca_auth_failed":"Unable to authorize! Please try again.","login_button_text":"Log in","login_form_has_an_error":"Please check and fix the errors above","uppercase_search":"Search","lowercase_search":"search","uppercase_page":"Page","lowercase_page":"page","uppercase_items":"Items","lowercase_items":"items","uppercase_per":"Per","lowercase_per":"per","uppercase_of":"Of","lowercase_of":"of","back":"Back to plans","zip_code_placeholder":"Zip\/Postal Code","download_file_button_text":"Download File","input_field_instructions":{"tip_amount":{"placeholder_text":"How much would you like to tip?","initial":{"instruction_type":"normal","instruction_message":"How much would you like to tip? Choose any currency."},"empty":{"instruction_type":"error","instruction_message":"How much would you like to tip? Choose any currency."},"invalid_curency":{"instruction_type":"error","instruction_message":"Please choose a valid currency."}},"recurring":{"placeholder_text":"Recurring","initial":{"instruction_type":"normal","instruction_message":"How often would you like to give this?"},"success":{"instruction_type":"success","instruction_message":"How often would you like to give this?"},"empty":{"instruction_type":"error","instruction_message":"How often would you like to give this?"}},"name":{"placeholder_text":"Name on Credit Card","initial":{"instruction_type":"normal","instruction_message":"Enter the name on your card."},"success":{"instruction_type":"success","instruction_message":"Enter the name on your card."},"empty":{"instruction_type":"error","instruction_message":"Please enter the name on your card."}},"privacy_policy":{"terms_title":"Terms and conditions","terms_body":null,"terms_show_text":"View Terms","terms_hide_text":"Hide Terms","initial":{"instruction_type":"normal","instruction_message":"I agree to the terms."},"unchecked":{"instruction_type":"error","instruction_message":"Please agree to the terms."},"checked":{"instruction_type":"success","instruction_message":"I agree to the terms."}},"email":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email address"},"success":{"instruction_type":"success","instruction_message":"Enter your email address"},"blank":{"instruction_type":"error","instruction_message":"Enter your email address"},"not_an_email_address":{"instruction_type":"error","instruction_message":"Make sure you have entered a valid email address"}},"note_with_tip":{"placeholder_text":"Your note here...","initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"empty":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"not_empty_initial":{"instruction_type":"normal","instruction_message":"Attach a note to your tip (optional)"},"saving":{"instruction_type":"normal","instruction_message":"Saving note..."},"success":{"instruction_type":"success","instruction_message":"Note successfully saved!"},"error":{"instruction_type":"error","instruction_message":"Unable to save note note at this time. Please try again."}},"email_for_login_code":{"placeholder_text":"Your email address","initial":{"instruction_type":"normal","instruction_message":"Enter your email to log in."},"success":{"instruction_type":"success","instruction_message":"Enter your email to log in."},"blank":{"instruction_type":"error","instruction_message":"Enter your email to log in."},"empty":{"instruction_type":"error","instruction_message":"Enter your email to log in."}},"login_code":{"initial":{"instruction_type":"normal","instruction_message":"Check your email and enter the login code."},"success":{"instruction_type":"success","instruction_message":"Check your email and enter the login code."},"blank":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."},"empty":{"instruction_type":"error","instruction_message":"Check your email and enter the login code."}},"stripe_all_in_one":{"initial":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"empty":{"instruction_type":"error","instruction_message":"Enter your credit card details here."},"success":{"instruction_type":"normal","instruction_message":"Enter your credit card details here."},"invalid_number":{"instruction_type":"error","instruction_message":"The card number is not a valid credit card number."},"invalid_expiry_month":{"instruction_type":"error","instruction_message":"The card's expiration month is invalid."},"invalid_expiry_year":{"instruction_type":"error","instruction_message":"The card's expiration year is invalid."},"invalid_cvc":{"instruction_type":"error","instruction_message":"The card's security code is invalid."},"incorrect_number":{"instruction_type":"error","instruction_message":"The card number is incorrect."},"incomplete_number":{"instruction_type":"error","instruction_message":"The card number is incomplete."},"incomplete_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incomplete."},"incomplete_expiry":{"instruction_type":"error","instruction_message":"The card's expiration date is incomplete."},"incomplete_zip":{"instruction_type":"error","instruction_message":"The card's zip code is incomplete."},"expired_card":{"instruction_type":"error","instruction_message":"The card has expired."},"incorrect_cvc":{"instruction_type":"error","instruction_message":"The card's security code is incorrect."},"incorrect_zip":{"instruction_type":"error","instruction_message":"The card's zip code failed validation."},"invalid_expiry_year_past":{"instruction_type":"error","instruction_message":"The card's expiration year is in the past"},"card_declined":{"instruction_type":"error","instruction_message":"The card was declined."},"missing":{"instruction_type":"error","instruction_message":"There is no card on a customer that is being charged."},"processing_error":{"instruction_type":"error","instruction_message":"An error occurred while processing the card."},"invalid_request_error":{"instruction_type":"error","instruction_message":"Unable to process this payment, please try again or use alternative method."},"invalid_sofort_country":{"instruction_type":"error","instruction_message":"The billing country is not accepted by SOFORT. Please try another country."}}}},"fetched_oembed_html":false}
3 Replies to “Checking the Release of Quarantined Messages”