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.

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…).

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.

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.


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.


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


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

Comments (38)

  1. 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. See
    http://technet.microsoft.com/en-us/library/jj200726.aspx for more information on these properties.

    1. ITvic says:

      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 2000
      Cannot 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

      1. 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?

        1. Andy Helsby says:

          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

          1. Andy Helsby says:

            I logged a ticket yesterday – Microsoft have confirmed they have lowered the limit and will be updating this article in a week or so.

  2. 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.

  3. No problem at all! I have a PowerShell section in my OneNote where I keep tidbits like this 🙂

  4. 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.

  5. Anonymous says:

    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

    so, I’m surprised it works for you, Andrew 🙂

  6. Hi Tim, good question. I don’t have a large enough quarantine in my test tenant to be able to test this so I’m not sure. If you have a lot of messages you may need to modify the script to run more of a loop rather than doing the whole release with a single
    line of PowerShell.

  7. 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.

  8. 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.

  9. 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.

  10. Hi Raj, what error are you seeing? Is it "Cannot validate argument on parameter ‘Identify’ ?

  11. BrianK says:

    Nice, thanks for this Andrew. I can see it coming in handy!

  12. Scott says:

    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??

  13. siddiq says:

    result of the Get-QuarantineMessage is not returning all the message in the quarantine area.

  14. Randeep Sahota says:

    Very helpful Andrew – that limit of 500 in the GUI had me scratching my head..
    nice one

  15. Tim C says:

    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

  16. Urs Rau says:

    HI, Very helpful. I have a "strange" problem. I don't seem to have access to the EOP cmdlet Release-QuarantineMessage ? If I run a get-command *quarantine* it only lists Get-QuarantineMessage and Get-QuarantineMessageHeader . Any idea what it takes for
    ne to get access to the Release-QuarantineMessage one as well?

    The strange thing is, on my outlook.office365.com/ECP/ page -> protection -> quarantine page I can't release quarantined messages, it simply says I don't have permisison, even though I am an admin.

    But on the personal admin page (
    https://admin.protection.outlook.com/quarantine ) I can release them, no problem.

    Maybe the Release-QuarantineMessage cmdlet is tied to a permission or role or group membership and once I have that box ticked correclty I can then use the cmdlet as well as release them from ecp?

    Any ideas?

  17. Anonymous says:

    I only began this blog in June of this year and so it’s hard to believe that it is already six

  18. Al Douglas says:

    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

  19. Lee Mayger says:

    I cannot believe that you can no longer release a message to an administrator for review, before deciding whether to release it to its intended recipient!!! I know that we can look at a message trace and see which filters are affecting the SCL, but if
    we do not see a reason as to why the SCL score is high then how are we supposed to investigate this?

  20. Raj says:

    Hi Andrew, How can I specify the startreceiveddate parameter to get the results for the Day.


  21. Raj says:

    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


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

    until ($Batchfile -eq $null)

  22. Raj says:

    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.

  23. 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.

  24. will says:

    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.

  25. 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.

  26. ktaber says:

    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

  27. James Jackson says:

    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.

  28. mpt says:

    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.


  29. ITvic says:

    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.

    1. 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.

      1. ITvic says:

        Andrew, the case # 616122091910753

  30. Rob says:

    Awesome information, THANKS for this article. Was able to release a large group of e-mails from a specific sender.

  31. Dave says:

    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.

  32. Leffingt says:

    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”

Skip to main content