Quarantine and PowerShell

The EOP online quarantine is a wonderful feature that can be easily managed through the Office 365 Portal. However, if you are looking for more functionality and flexibility with quarantine management, you’ll need to turn to PowerShell.

Before I begin I have a quick disclaimer. The PowerShell scripts in this article are not official, nor are they supported by Microsoft. They are just scripts that I have written and use myself in my day to day work with EOP.

Current Limitations of the Office 365 Portal

For most of the day to day quarantine tasks the Office 365 Portal view will work just fine. This view will allow you to search for messages based on a wide range of criteria and release single messages to either a few or all the intended recipients.

There are a few places where the Office 365 Portal view falls short though.

  1. There is currently no way to mass release messages from the quarantine. For example, let’s say there are hundreds of quarantined messages from a particular sender in the quarantine and you would like to release all of them. Through the Office 365 Portal you can only release one at a time which is very cumbersome.

  2. The portal will only show a maximum of 500 entries and there is no “next page” button.” If you have a large quarantine in excess of 500 messages, you will need to use the filtering options to limit your search criteria.

  3. There is currently no way to see if a message has been released unless you double click the message to bring up the properties. If “Release to…” is greyed out, then the message has already been released.


PowerShell cmdlets

The following three cmdlets are currently available for quarantine management.

QuarantineMessage
This will retrieve a list of messages in the quarantine which can be filtered with a wide range of search criteria (recipient address, sender address, subject, etc…).

Get-QuarantineMessageHeader
Returns the header of a message in the quarantine. This is the same as highlighting a quarantined message and clicking “View message header…” in the Office 365 Portal.

Release-QuarantineMessage
Messages piped to this cmdlet from Get-QuarantineMessage will be released. This cmdlet can be used to release a message to either some, or all of the intended recipients.

See Exchange Online Protection cmdlets for a complete list.


Technicalities

The Get-QuarantineMessage cmdlet returns a number of properties, but there are three that will always be empty unless you specify the Identity parameter when calling this cmdlet. These three properties are as follows.

  • RecipientAddress
  • QuarantinedUser
  • ReleasedUser

Let’s look at an example. Here I am using Get-QuarantineMessage by specifying the message subject and not the message identify. Take note of the empty properties.

Now I have used Get-QuarantineMessage to target the same message as above, but instead I have referenced it using its identity. In this case the above three properties all contain data.


 
We can now see that this message was sent to two recipients and has been released to one of them but not the other.

One more item I’d like to note. Messages can only be released to email address that were on the original recipient list. This means that you cannot release a message to an admin for review unless the admin was one of the intended recipients. This is true for both the portal view as well as with PowerShell.

Examples

In the following examples I am not limiting the results returned by Get-Quarantine. In a production environment with a quarantine that contains many messages I would recommend filtering the returned results. For example, to restrict the results returned to a single day the following should be used in the examples in this section.

Get-QuarantineMessage –StartReceivedDate 07/20/2014 –EndReceivedDate 07/21/2014

Further filtering may be required with the PageSize property.

View quarantined items that have not yet been released

When a message is released from the quarantine, it will still remain in the quarantine until it expires. This makes it difficult to tell if a message has been released yet or not. Through the portal, you need to bring up the properties of a message to find this out which can be very cumbersome. This can easily be accomplished with PowerShell.

(Get-QuarantineMessage).identity | ForEach {Get-QuarantineMessage -Identity $_} | Where {$_.QuarantinedUser -ne $null}

Here I am looking at the QuarantinedUser property and unless its empty, the message will be returned by this query.

View items that have already been released

This is similar to the above script except here I’m focusing on messages that have already been relased.

(Get-QuarantineMessage).identity | ForEach {Get-QuarantineMessage -Identity $_} | Where {!$_.QuarantinedUser} 

Again I am looking at the QuarantinedUser property, but in this case if it’s empty the message will be returned.

Mass release messages from the quarantine

Let’s say that we’ve created a rule which quarantined all messages sent by joe@contoso.com and we want to mass release them.

Get-QuarantineMessage –SenderAddress joe@contoso.com | Release-QuarantineMessage -ReleaseToAll

Note that this will produce a warning for any message that has already been released but this will not stop the execution of the command.

Export a list of quarantined items to a csv file

Let’s say you want to export a list of quarantined messages to a CSV file for analysis. The following will accomplish this and note that I am specifying the identity parameter to ensure all the properties are returned.

(Get-QuarantineMessage).identity |foreach {Get-QuarantineMessage -Identity $_} |export-csv test.csv -notypeinformation

Resources

Connect to EOP PowerShell – If you have EOP only.
Connect to Exchange Online PowerShell – If you have Exchange Online.
EOP cmdlets

Comments

  • Anonymous
    January 01, 2003
    Hi there Siddiq. If you have a lot of messages in your quarantine, you will have to use the -Page & -PageSize properties of the Get-QuarantineMessage cmdlet. The default PageSize is 1000, but configurable up to 5000. Seehttp://technet.microsoft.com/en-us/library/jj200726.aspx for more information on these properties.
    • Anonymous
      January 03, 2017
      That doesn't seem to be the case anymore. I don't know when this changed but I can't specify -PageSize larger than 1000.> Get-QuarantineMessage -PageSize 2000Cannot validate argument on parameter 'PageSize'. The 2000 argument is greater than the maximum allowed range of 1000.Supply an argument that is less than or equal to 1000 and then try the command again. + CategoryInfo : InvalidData: (:) [Get-QuarantineMessage], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationError,Get-QuarantineMessage + PSComputerName : outlook.office365.com
      • Anonymous
        January 10, 2017
        ITvic, that is very interesting. According to https://technet.microsoft.com/en-us/library/jj200726(v=exchg.160).aspx, you can specify a pagesize larger than 1000. Could you open a ticket with us on this?
        • Anonymous
          April 13, 2017
          I've run up against the same problem and just logged a ticket. Not only does it not accept values higher than 1000, the default page size is also 100 not 1000.I use the following function to quickly get stats for the past 24hrs. Less than 12 hrs after turning on mail flow to Office365 I'm already over 1000 hits..function get-spamstats{ $a=get-quarantinemessage -StartReceivedDate (get-date).adddays(-1) -EndReceivedDate (get-date) -pagesize 999"Past 24hrs " + $a.count"False Positives " + ($a | ? {$.reported -eq $true}).count"Bulk " + ($a | ? {$.type -eq "Bulk"}).count"Phish " + ($a | ? {$_.type -eq "Phish"}).count}
          • Anonymous
            April 14, 2017
            I logged a ticket yesterday - Microsoft have confirmed they have lowered the limit and will be updating this article in a week or so.
  • Anonymous
    January 01, 2003
    Thanks for sharing Lars, that is very interesting. Without seeing the environments I can't say why you are having issues getting it to work. My test environments have a very small amount of messages in the quarantine which may be playing in to this.
  • Anonymous
    January 01, 2003
    No problem at all! I have a PowerShell section in my OneNote where I keep tidbits like this :)
  • Anonymous
    January 01, 2003
    Hi Urs, good question. Looking at http://technet.microsoft.com/en-us/library/jj200673(v=exchg.150).aspx, you will need the "Organization Management" and "Hygiene Management" roles to be able to release items from the quarantine. If your permissions appear to be ok, open a support ticket as we will be able to dig deeper into the problem. If you are a Global Admin and seeing this, definitely open a support ticket as you should see all three cmdlets.
  • Anonymous
    January 01, 2003
    Yeah, same issue as Scott

    Cannot validate argument on parameter 'Identity'. The argument is null or empty.

    (Get-QuarantineMessage -SenderAddress xyz@domain.com).Identity returns $null (with or without the SenderAddress parameter), which explains the exception.

    Get-QuarantineMessage -SenderAddress xyz@domain.com | select Identity returns the expected result set.

    Also, if I dump all quarantined messages into a list (Get-QuarantineMessage -senderaddress xyz@domain.com), the RecipientAddress, QuarantinedUser and ReleasedUser properties are all $null - it's not until I retrieve individual quarantined messages using their respective Identity property that those field are populated.

    Therefore, this works, but takes FOREVER (essentially same logic, just not a one-liner):
    $qm = Get-QuarantineMessage -senderaddress xyz@domain.com
    $qm | ForEach {Get-QuarantineMessage -Identity $.Identity} | ? {$.QuarantinedUser -ne $null}


    Also, doesn't matter whether I connect to

    https://ps.protection.outlook.com/powershell-liveid/ or
    https://outlook.office365.com/powershell-liveid/

    so, I'm surprised it works for you, Andrew :)
  • Anonymous
    January 01, 2003
    The comment has been removed
  • Anonymous
    January 01, 2003
    Hi Scott, based on that error I'm not sure. I just tried running the the following against my Exchange Online tenant and didn't have any issues.

    (Get-QuarantineMessage).identity |foreach {Get-QuarantineMessage -Identity $_} |export-csv test.csv -notypeinformation

    I have seen some strange PowerShell issues that ended up being attributed to either a group policy or a firewall issue when run from a corporate network. Do you have issues with any other Exchange Online PowerShell commands? Maybe try running on a machine that doesn't have a GPO being applied and that has a pure internet connection.
  • Anonymous
    January 01, 2003
    Hi Lee, you are correct in that currently an administrator cannot view the body of a quarantined message, nor can they release it to themselves (unless they were one of the original recipients) for review. Stay tuned we are looking to address this.

    Currently, your best bet is looking at the headers of a message that has been quarantined, looking for things like spoofing along with how EOP stamped the message. Tracing the message and looking for transport rules that triggered, as you had mentioned, is also another great way.

  • Anonymous
    January 01, 2003
    Hi there Raj. Typically the startReceivedDate parameter is used in conjunction with the endReceivedDate parameter. Using these, the PowerShell to see all messages quarantined on a particular day would look something like this.

    Get-QuarantineMessage -StartReceivedDate "03/30/2015 0:00" -EndReceivedDate "03/30/2015 23:59"

    This will show me all messages that were quarantined on March 30th of this year. The exact format of the date and time will need to match your computers regional settings.
  • Anonymous
    January 01, 2003
    Hi Raj, what error are you seeing? Is it "Cannot validate argument on parameter 'Identify' ?
  • Anonymous
    August 14, 2014
    Nice, thanks for this Andrew. I can see it coming in handy!
  • Anonymous
    September 09, 2014
    I'm trying to run the command to export all the quarantined messages but its not working for me. I receive a "cannot validate argument on parameter 'Identity" error. What am I doing wrong??
  • Anonymous
    September 11, 2014
    result of the Get-QuarantineMessage is not returning all the message in the quarantine area.
  • Anonymous
    October 11, 2014
    The comment has been removed
  • Anonymous
    November 19, 2014
    I'm running the ' Get-QuarantineMessage –SenderAddress joe@contoso.com | Release-QuarantineMessage -ReleaseToAll ' and it runs great on the first 20 - 50 items and then stops. Anyway to use the -resulteSize unlimited to have the script check all the quarantined items?
  • Anonymous
    December 12, 2014
    The comment has been removed
  • Anonymous
    December 24, 2014
    I only began this blog in June of this year and so it’s hard to believe that it is already six
  • Anonymous
    January 02, 2015
    If you are experiencing problems with the "Cannot validate argument on parameter 'Identity'. The argument is null or empty.", I would check that you're using PS version 4 (or at least 3), I seem to remember hitting similar issues in the past
    Check PS version using Get-Host and look for version in results
    PS version 4.0 Available as part of http://www.microsoft.com/en-gb/download/details.aspx?id=40855
  • Anonymous
    February 11, 2015
    The comment has been removed
  • Anonymous
    April 03, 2015
    Hi Andrew, How can I specify the startreceiveddate parameter to get the results for the Day.

    Thanks
  • Anonymous
    May 04, 2015
    Thanks for the update Andrew. I have tried like below to schedule the Quarantined email report for every one hour, which is not working.

    $dateEnd = get-date
    $dateStart = $dateEnd.AddHours(-1)

    $output = Get-Date -Format ddMMMyyyy

    $Batchfile = $null

    $Page = 1

    do

    {
    $Batchfile = (Get-QuarantineMessage -StartReceivedDate $dateStart -EndReceivedDate $dateEnd -PageSize 5000 -Page $Page).identity| foreach {Get-QuarantineMessage -Identity $_} |Export-Csv D:EOPlogsQuarantinedEmailLogs$output.csv -NoTypeInformation -Append

    $Page++
    }
    until ($Batchfile -eq $null)
  • Anonymous
    May 19, 2015
    Cannot Validate on Argument on Parameter 'Identity'. The Argument is null or empty. Supply an argument that is not null or empty and then try the command again.
  • Anonymous
    June 12, 2015
    Hi Raj, that is actually expected based on the way that script is written. The script will keep looping until there are no further messages in the quarantine. The loop will run one more time after all messages have been parsed, which will result in the "Cannot validate argument on parameter 'Identify." You could tweak the script to be smarter about when to exit the loop. All messages it parses before the end are just fine, the script just doesn't exit gracefully.
  • Anonymous
    September 02, 2015
    I am trying to search the EOP Quarantine with powershell as it supports wildcards in the recipient address field. I am running into issues as evidenced in the article, with the recipient address fields coming up empty. I am a new powershell user trying to find a quick command to search by recipient (To:) address. Any help is greatly appreciated.
  • Anonymous
    October 09, 2015
    Hi Will, this should do the trick for you.

    (Get-QuarantineMessage).identity | ForEach {Get-QuarantineMessage -Identity $} | Where {$.RecipientAddress -eq "someone@contoso.com"}

    If you have a large amount of messages in your quarantine, you may need to tweak this to limit the results.
  • Anonymous
    January 15, 2016
    I want to export a list with the Recipient address. How do I go about this with PowerShell?This gets me a list of messages for a specific domain I'm looking for, but doesn't tell me who the intended Recipient is... since it needs the Identity of the message:Get-QuarantineMessage | ? {$_.Senderaddress -like "*@contoso.com"} | Export-Csv C:\Quarantine.csv
  • Anonymous
    June 09, 2016
    Hello, Is it possible to find when and who released a quarantined message if it was released by mistake. Does auditing have to be enabled? Thanks in advance for any responses.
  • Anonymous
    July 15, 2016
    Hi I would like to pull meesages that are flagged with a malicious and the malicous attachment. How can this be done in a safe manner. Thanks
  • Anonymous
    January 10, 2017
    I already opened the ticket with Office 365 team. Here is their explanation for the change:Hello Victor, Hope you are doing well. As discussed on call today (1/4/2016) you want to know why the PageSize limit is reduced from 5000 to 1000 ?We have checked with the backend team and got the below answer• Reducing the PageSize limit helps in database load and general performance. • Based on the change that is "restricting page size to 1000" helps to improve performance an prevent getting quarantined messages with very large result sets in a single request and avoid unnecessary load on the Servers.
    • Anonymous
      January 10, 2017
      ITvic. That is very interesting. Can you provide the case number for that case? Once I get the case number and can confirm that, I'll submit a request to have our documentation updated to note the 1000 limit.
      • Anonymous
        January 10, 2017
        Andrew, the case # 616122091910753
  • Anonymous
    February 09, 2017
    Awesome information, THANKS for this article. Was able to release a large group of e-mails from a specific sender.
  • Anonymous
    April 20, 2017
    If I want to provide a list of recipients of messages from a specific sender, do I have to provide message ID for every one? I guess I could dump out the results and do it in two steps, but that seems clunky.
  • Anonymous
    March 23, 2018
    Here is just one way to overcome the 1000 limit without having to run a script multiple times and switching pages...If (!(Test-Path -path "C:\Scripts\O365-Exchange\EOP\Reports")) {New-Item "C:\Scripts\O365-Exchange\EOP\Reports" -type directory} # creates directory if it does not exist$FNdate = $(get-date -f yyyy-MM-dd) # this date variable is used for appending to filename exports.$script:page = 0 Function DateParams { $dateEnd = Get-Date $Script:dateEnds = $dateEnd.ToShortDateString() [int]$StartDate = Read-Host "How many days back you wanna go?" $hours = $StartDate * 24 $dateStart = $dateEnd.AddHours(-$hours) $Script:dateStarts = $dateStart.ToShortDateString() }Function GetQuarantinedMessagesUntil { if (!$dateEnd){& DateParams} do { $page ++ #= Read-Host "Which Page Number" $Qmessages = Get-o365QuarantineMessage -PageSize 1000 -Page $page -StartReceivedDate $dateStarts -EndReceivedDate $dateEnds | select * $Qmessages | ft Write-host "" Write-Host "Found " -NoNewline Write-Host "$($Qmessages.count) " -NoNewline -ForegroundColor Green Write-Host "quarantined messages from " -NoNewline Write-Host "$dateStarts " -NoNewline -ForegroundColor Green Write-Host "to " -NoNewline Write-Host "$dateEnds " -ForegroundColor Green Write-Host "Page: $page" -ForegroundColor Yellow $Qmessages | Export-Csv C:\Scripts\O365-Exchange\EOP\Reports$FNdate-Quarantine-Report.csv -Append -NoTypeInformation } until ($Qmessages.count -lt 1000) $dateEnd | Out-Null Write-host "Report: C:\Scripts\O365-Exchange\EOP\Reports$FNdate-Quarantine-Report.csv"}
  • Anonymous
    March 14, 2019
    Hi,Workaround for this limitation:$count = 1while ($count -le 10000){Get-QuarantineMessage -PageSize 1000 -Page $count$count++}Best Regards,