White Paper: Remove Specific Messages from Exchange Server

 

Angelique Conde, SR Support Escalation Engineer. Ed Bringas, SR Support Escalation Engineer

May 2011

Summary

This document describes how to remove specific messages from several mailboxes or from the transport queues in a Microsoft Exchange environment. This includes a mixed Exchange environment in which different versions of Exchange are used.

Applies To

Microsoft Exchange Server 2010 Service Pack 1 (SP1)

Microsoft Exchange 2010

Microsoft Exchange Server 2007

Microsoft Exchange Server 2003

Contents

  • Remove Messages from MailboxesRemove Messages from Mailboxes

    • Permissions

    • Examples

  • Remove Messages from Public Folders

  • Remove Messages from Mail Queues

  • Use a Transport Rule to Prevent Message Delivery

Introduction

Occasionally, a Microsoft Exchange administrator may experience a situation in which a specific message or messages must be bulk-removed from several mailboxes or from the Exchange transport pipeline. For example, a situation may occur in which a sensitive message is unintentionally sent to a distribution group or to specific mailboxes, or an administrator might have to clean up after a mass mailing issue.Although the information shown here is available elsewhere for each specific version of Exchange, this document attempts to create a single comprehensive document that covers the task of removing specific messages in an environment in which you may be running multiple versions of Exchange. In this scenario, it may be difficult to locate or refer to multiple documents that target only a particular version of Exchange. Therefore, the examples shown here provide more generalized instructions about how to remove specific messages from an environment that may be running any of the currently supported versions of Exchange.

Remove Messages from Mailboxes

Exchange supports the following methods to bulk-remove messages from Exchange:

  • Exchange Server 2010 Service Pack 1 (SP1) In Exchange 2010 SP1, you can use the Search-Mailbox cmdlet to search for and remove messages from mailboxes.

  • Exchange Server 2010 or Exchange Server 2007 In the original release version (RTM) of Exchange 2010 or in Exchange 2007, you can use the Export-Mailbox cmdlet to search for and remove messages from mailboxes. This cmdlet can export mailbox content based on folder names, a date and time range, file names, or other criteria.

  • Exchange Server 2003In Exchange 2003 or in earlier versions of Exchange, you can use the ExMerge utility to extract items from mailboxes.

Permissions

To use the Export-Mailbox cmdlet in Exchange 2010 or the Search-Mailbox cmdlet in Exchange 2010 SP1, you must have the Mailbox Import Export role-based access control (RBAC) role assigned. You can use the Get-ManagementRoleAssignment cmdlet to view the role groups or management roles that are assigned to your account. For more information, see Understanding Role Based Access Control.

The account used to export data by using the Export-Mailbox cmdlet must be an Exchange administrator, must be a member of the local Administrators group on the target server, and must have the Full Access mailbox permission assigned on the source and target mailboxes. Also, the target mailbox that you specify during the export operation must already exist.

Note

Although the target mailbox must already exist, the target folder that is specified in the export command is automatically created in the target mailbox during the export operation.

To retrieve all mailboxes from an organization and assign the Full Access mailbox permission, run the following command at the Exchange Management Shell:

Get-Mailbox -ResultSize unlimited | Add-MailboxPermission -User ExchAdmin -AccessRights FullAccess -InheritanceType all

This command assigns the Full Access mailbox permission to an account named ExchAdmin. This action gives the ExchAdmin account rights to export and delete messages from the users’ mailboxes.

Note

If you have to export or delete messages from only a few mailboxes, you can use the Get Mailbox cmdlet together with an appropriate filter. Alternatively, you can specify each individual source mailbox.

After you export or delete the messages from the appropriate mailboxes, remove the Full Access permission for the account. To do this, run the following command at the Exchange Management Shell:

Get-Mailbox -ResultSize unlimited | Remove-MailboxPermission -User MyAdmin -AccessRights FullAccess -InheritanceType all

Examples

The following examples illustrate the removal of messages from mailboxes by using the various commands in Exchange.

Exchange Server 2010 SP1

Exchange 2010 SP1 uses the Search-Mailbox cmdlet to search for and/or remove messages from mailboxes. The Search-Mailbox cmdlet supports Exchange Search keywords and uses the –DeleteContent parameter to remove messages. For more information, about Exchange Search, see Understanding Exchange Search.

For detailed information and examples about how to use the Search-Mailbox cmdlet to search for and remove messages, see Use Mailbox Search to Delete Messages.

Exchange Server 2010 RTM or Exchange Server 2007

The RTM version of Exchange 2010 and of Exchange 2007 use the Get-Mailbox cmdlet to search for and remove messages from mailboxes.

The following command removes from the Inbox folder of all mailboxes on Server1 all messages that have the subject keyword "Confidential note" and that were received between May 7, 2011 and May 9, 2011. In this example, the messages will be deleted from the mailboxes and copied to a folder named DeleteMsgs in a mailbox that is named MyBackupMailbox. After you run this command, an Exchange administrator who has access to the MyBackupMailbox mailbox may review the messages.

Note

In this command, the StartDate and EndDate parameters must match the date format setting on the server, whether it is MM-DD-YYYY or DD-MM-YYYY.

Get-Mailbox -Server Server1 -ResultSize Unlimited | Export-Mailbox -SubjectKeywords "Confidential note" -IncludeFolders "\Inbox" -StartDate "05/07/2011" -EndDate "05/09/2011" -DeleteContent -TargetMailbox MyBackupMailbox -TargetFolder DeleteMsgs -Confirm:$false

The following command removes all messages that contain the words “Surprise Party” in the Subject field or message body from all mailboxes in the organization:

Get-Mailbox -ResultSize Unlimited | Export-Mailbox -ContentKeywords "Surprise Party" -TargetMailbox MyBackupMailbox -TargetFolder 'SurprisePartyFolder' -DeleteContent

In this example, the messages will be deleted from all mailboxes and copied to the folder named SurprisePartyFolder in a mailbox that is named MyBackupMailbox. After you run this command, an Exchange administrator who has access to the MyBackupMailbox mailbox may review the messages.

Tip

Depending on the size of the environment, it may be better to perform the message export and removal operation in batches. To do this, use the Get-Mailbox cmdlet together with the Server or Database parameters. For example, Get-Mailbox -Server <ServerName> -ResultSize Unlimited or Get-Mailbox -Database <DB_Name> -ResultSize Unlimited. Alternatively, specify a filter by using the Filter parameter. You can also use the Get-DistributionGroupMember cmdlet to perform this operation against members of a distribution group.

We recommend that you always use the TargetMailbox and TargetFolder parameters to specify a destination mailbox when you export messages. This lets you review the messages before purging them from the e-mail system. In this scenario, you can import any legitimate messages back to the appropriate mailboxes, if required. However, you do not have to specify a destination mailbox when you run these commands. Instead, you can delete them from the users’ mailboxes without first copying the messages to a backup mailbox. The following command deletes all messages that contain “Surprise Party” in the Subject field or message body from all mailboxes. This command does not copy the messages to a backup mailbox.

Get-Mailbox | Export-Mailbox -ContentKeywords "Surprise Party" -DeleteContent

It is best that you define a narrow search criteria when you search for messages. This will help reduce the likelihood o unintentionally deleting legitimate messages. For more information about the syntax or parameters used in the Export-Mailbox cmdlet, see the following topics:

How to Export Mailbox Data

Export Mailbox Data Examples

Exchange Server 2003

Exchange 2003 and earlier versions of Exchange use the ExMerge utility to extract mail items from mailboxes located on legacy Exchange versions. For more information about how to use ExMerge, see Microsoft Knowledge Base article 328202, HOW TO: Remove a Virus-Infected Message from Mailboxes by Using the ExMerge.exe Tool.

Remove Messages from Public Folders

The following methods are available to remove messages from a public folder:

  • Outlook Object Model (OOM) This method has the advantage of working with public folders that are hosted on Exchange 2010, on Exchange 2007 or on Exchange 2003. However, OOM does not permit detailed control over how to retrieve items from a public folder. Therefore, depending on the number of items that must be parsed, a script that uses this method to remove messages from a public folder may run very slowly.

  • **Exchange Web Services (EWS)**This method is available for public folders that are hosted on servers running Exchange 2010 or Exchange 2007. EWS is able to use a ranged retrieval of messages. Therefore, it may run much more quickly against large public folders.

The following example illustrates a PowerShell script that uses OOM. This script must be run from a computer that is running Microsoft Outlook.

Note

This script will work together with Microsoft Outlook 2010, Microsoft Office Outlook 2007, or Microsoft Outlook 2003. Additionally, this script should work on a computer that is running PowerShell 2.0.

# Delete-PublicFolderItems.ps1 
# Syntax: 
# .\Delete-PublicFolderItems \TopFolder\Subfolder $false 
# This example runs against a single folder and does not recurse. 
# .\Delete-PublicFolderItems \"\" $true 
# This example runs against the entire public folder hierarchy.
 
param([string]$folderPath, [bool]$recurse)
 
# By default, this script only reports what it would have deleted. 
# When you change the $readOnly variable to $false, THIS SCRIPT WILL DELETE ITEMS OUT OF the 
# PUBLIC FOLDERS. 
# Before you use this script,
# MAKE SURE THAT YOU READ THROUGH IT COMPLETELY
# MAKE SURE TO MODIFY THE $item.Subject.Contains VALUE
# MAKE SURE THAT YOU TEST IT IN A TEST ENVIRONMENT AND THAT YOU HAVE A BACKUP
$readOnly = $true
 

$outlook = new-object -com Outlook.Application 
$mapi = $outlook.GetNamespace("MAPI") 
$pfStore = $null 
foreach ($store in $mapi.Stores) 
{ 
    if ($store.ExchangeStoreType -eq 2) 
    { 
        $pfStore = $store 
    } 
}
 
if ($pfStore -eq $null) 
{ 
    "Could not find public folder store." 
    return 
}
 
$pfRoot = $pfStore.GetRootFolder()
 
# Define the function we will use to traverse 
# the Public Folder tree if a path was specified. 
function GetNamedFromCollection($name, $collection) 
{ 
     foreach ($item in $collection) 
     { 
          if ($item.Name -eq $name -or $item.DisplayName -eq $name) 
          { 
               return $item 
          } 
     } 
     return $null 
}
 
$allPFs = GetNamedFromCollection "All Public Folders" $pfRoot.Folders 
if ($allPFs -eq $null) 
{ 
     "Could not find the All Public Folders folder." 
     return 
}
 
$folderPath = $folderPath.Trim("\") 
$folderPathSplit = $folderPath.Split("\") 
$folder = $allPFs 
if ($folderPath.Length -gt 0) 
{ 
     "Traversing folder path..." 
     for ($x = 0; $x -lt $folderPathSplit.Length; $x++) 
     { 
          $folder = GetNamedFromCollection $folderPathSplit[$x] $folder.Folders 
     } 

     if ($folder -eq $null) 
     { 
          ("Could not find folder: " + $folderPath) 
          return 
     } 

     ("Found folder: " + $folder.FolderPath) 
}
 
# Define the function that will perform the removal
 
function DoFolder($folder) 
{ 
    ("Scanning folder: " + $folder.FolderPath) 
    
    try 
    { 
        foreach ($item in $folder.Items) 
        { 
            ########################################################## 
            # This is the most important part. This is where we 
            # decide whether to delete the item. 
            # By default this script will delete any item that 
            # contains the phrase \"Here you have\" in the subject. 
            # If that isn't what you want, you need to edit 
            # the criteria here. 
            if ($item.Subject.Contains("Here you have")) 
            { 
                if ($readOnly) 
                { 
                    ("     Would have deleted item: " + $item.Subject) 
                } 
                else 
                { 
                    ("     Deleting item: " + $item.Subject) 
                    $item.Delete() 
                } 
            } 
        } 
    } 
    catch { "Error processing folder." } 
    
    if ($recurse) 
    { 
        foreach ($subfolder in $folder.Folders) 
        { 
            try 
            { 
                DoFolder $subfolder 
            } 
            catch { "Error processing folder: " + $subfolder.FolderPath } 
        } 
    } 
}
 
DoFolder $folder
 
"Done!"

The following example illustrates a PowerShell script that uses EWS through the EWS Managed API. This script can be run against public folders that are hosted on Exchange 2010 or on Exchange 2007. You can this script from a PowerShell 2.0 prompt on a computer that is running the EWS Managed API. You do not have to have Exchange Management Tools installed. For more information about how to obtain the Exchange Web Services Managed API, see Exchange Web Services Managed API.

# Delete-PublicFolderItems.ps1
# This script requires PowerShell 2.0 and .NET Framework 3.5. 
# Before you use this script,
# MAKE SURE THAT YOU READ THROUGH IT COMPLETELY
# MAKE SURE TO MODIFY THE $item.Subject.Contains VALUE
# MAKE SURE THAT YOU TEST IT IN A TEST ENVIRONMENT AND THAT YOU HAVE A BACKUP
# Syntax: 
# .\Delete-PublicFolderItems <email address for autodiscover> <path to folder> <recurse> 
# Examples: 
# .\Delete-PublicFolderItems administrator\@contoso.com \TopLevelFolder\Subfolder $false 
# This example runs against one specific folder and does not recurse. 
# .\Delete-PublicFolderItems administrator\@contoso.com \"\" $true 
# This example recursively runs against every folder in the PF hierarchy.
# The e-mail address you specify should typically be the email address of the administrator account 
# that you have used to log on as. Otherwise, you may receive Autodiscover errors.
 
param([string]$autoDiscoverEmail, [string]$folderPath, [bool]$recurse)
 
# Remember to install the EWS managed API from the following location: 
# https://www.microsoft.com/downloads/en/details.aspx?displaylang=en&FamilyID=c3342fb3-fbcc-4127-becf-872c746840e1 
# Then point the following command to the location of the DLL:
 
Import-Module -Name "C:\Program Files\Microsoft\Exchange\Web Services\1.0\Microsoft.Exchange.WebServices.dll"
 
# If the $readOnly variable is set to $true, the script runs in READ-ONLY mode. The script reports on the items that would be deleted.
# However, the script does not delete any items. 
# If the $readOnly variable is set to $false, the script will DELETE ITEMS OUT OF THE PUBLIC FOLDERS. 
# Make sure you test this first and that you have a backup of the public folder(s).
 
$readOnly = $true
 
# Use AutoDiscover to get the ExchangeService object
 
$exchService = new-object Microsoft.Exchange.WebServices.Data.ExchangeService([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2007_SP1) 
if ($exchService -eq $null) 
{ 
    "Could not instantiate ExchangeService object." 
    return 
} 
$exchService.UseDefaultCredentials = $true 
$exchService.AutodiscoverUrl($autoDiscoverEmail) 
if ($exchService.Url -eq $null) 
{ 
    return 
}
 
# Bind to the public folders
 
$pfs = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchService, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::PublicFoldersRoot) 
if ($pfs -eq $null) 
{ 
    return 
}
 
# Define the function that will be called to process the folders.
 
function DoFolder([Microsoft.Exchange.WebServices.Data.Folder]$folder, [string]$path) 
{ 
    ("Scanning folder: " + $path) 
    
     try 
     { 
        # Scan the items in this folder 
        $offset = 0; 
        $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset) 
        while (($results = $folder.FindItems($view)).Items.Count -gt 0) 
        { 
            foreach ($item in $results) 
            { 
                ############################################################ 
                # This is the most important part. This is where the script
                # determines whether to delete the item or not. 
                # By default the script will delete any item that contains 
                # the phrase \"Here you have\" in the subject. If this is not 
                # what you want, you must edit the criteria. 
                if ($item.Subject.Contains("Here you have")) 
                { 
                    if ($readOnly) 
                    { 
                        ("    Would have deleted item: " + $item.Subject) 
                    } 
                    else 
                    { 
                        ("    Deleting item: " + $item.Subject) 
                        $item.Delete([Microsoft.Exchange.WebServices.Data.DeleteMode]::HardDelete) 
                    } 
                } 
                ############################################################ 
            } 

            $offset += $results.Items.Count 
            $view = new-object Microsoft.Exchange.WebServices.Data.ItemView(100, $offset) 
        }
 
        if ($recurse) 
        { 
            # Recursively do subfolders 
            $folderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(2147483647) 
            $subfolders = $folder.FindFolders($folderView)    
            foreach ($subfolder in $subfolders) 
            { 
                try 
                { 
                    DoFolder $subfolder ($path + "\" + $subfolder.DisplayName) 
                } 
                catch { "Error processing folder: " + $subfolder.DisplayName } 
            } 
        } 
     } 
     catch 
     { 
         throw 
     } 
}
 
# Call DoFolder on the top folder. 
# If $recurse is true, it will recursively call itself.
 
if ($folderPath.Length -lt 1) 
{ 
    DoFolder $pfs "" 
} 
else 
{ 
    $folderPathTrim = $folderPath.Trim("\") 
    $folderPathSplit = $folderPathTrim.Split("\") 
    $tinyView = new-object Microsoft.Exchange.WebServices.Data.FolderView(2) 
    $displayNameProperty = [Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName 
    $thisFolder = $pfs 
    for ($x = 0; $x -lt $folderPathSplit.Length; $x++) 
    { 
        $filter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo($displayNameProperty, $folderPathSplit[$x]) 
        $results = $thisFolder.FindFolders($filter, $tinyView) 
        if ($results.TotalCount -gt 1) 
        { 
            "Ambiguous folder name." 
            return 
        } 
        elseif ($results.TotalCount -lt 1) 
        { 
            "Folder not found." 
            return 
        } 
        $thisFolder = $results.Folders[0] 
    } 
    
    DoFolder $thisFolder $folderPath 
}
 
# When we return here, we're done!
 
"Done!"

Note

This script accesses public folders by using EWS, which is a client API. Therefore, to see and delete items in the public folder, you must have the appropriate client permissions for the particular public folder. Client permissions differ from Exchange administrator permissions. For more information, see Public Folder Admin Permissions Versus Client Permissions.

Remove Messages from Mail Queues

In certain situations, you may have to purge messages from the Exchange messaging queues to help prevent the delivery of unwanted e-mail messages. For more information, see Understanding Transport Queues.

The following sections illustrate the removal of messages from the messaging queues by using the various methods available in Exchange.

Exchange Server 2010 and Exchange Server 2007

In Exchange 2010 and Exchange 2007, the removal of messages from the transport queues is a two part process. First, you must suspend the particular message or messages in the queues. Then, you can remove the messages from the queues. The following examples illustrate the suspension and removal of messages that contain certain words in the Subject field of the message.

Exchange Server 2010 RTM and Exchange Server 2007 SP3

This command suspends messages that have the string "Surprise Party" in the Subject field in all queues on Hub Transport servers:

Get-TransportServer | Get-Queue | Get-Message -ResultSize unlimited | where {$_.Subject -eq "Surprise Party"} | Suspend-Message

This command removes messages that have the string "Surprise Party" in the Subject field in all queues on Hub Transport servers:

Get-TransportServer | Get-Queue | Get-Message -ResultSize unlimited | Where {$_.Subject -eq "Surprise Party"} | Remove-Message -WithNDR $False

Note

You can run these commands against an individual Hub Transport server. To do this, specify the server name after Get-TransportServer. For more information, see Get-TransportServer.

You can also suspend and remove messages from a particular queue. To retrieve a list of queues on a transport server, use the Get-Queue cmdlet.

Use the following command to suspend messages that have the string "Surprise Party" in the Subject field of messages in a particular queue:

Get-Message -Queue "<server>\<queue>" -ResultSize unlimited | where{$_.Subject -eq "Surprise Party"} | Suspend-Message

This example removes suspended messages that have the string "Surprise Party" in the Subject file from a particular queue:

Get-Message -Queue "<server>\<queue>" -ResultSize unlimited | where{$_.Subject -eq "Surprise Party" } | Remove-Message -WithNDR $False

Exchange Server 2007 SP2 and Exchange Server 2007 SP1

For computers that are running Exchange 2007 SP2 or Exchange 2007 SP1, the following command suspends messages that have "Surprise Party" in the Subject field from transport queues on all Hub Transport servers in the Exchange organization:

Get-TransportServer | Get-Queue | Get-Message -ResultSize unlimited | where{$_.Subject -eq "Surprise Party" -and $_.Queue -notlike "*\Submission*"} | Suspend-Message

Note

On servers that are running the original release version of Exchange 2007 (RTM), of Exchange 2007 SP1 or of Exchange 2007 SP2, you cannot suspend or remove messages that are held in the Submission queue.

This command removes all suspended messages from transport queues other than the Submission queue:

Get-TransportServer | Get-Queue | Get-Message -ResultSize unlimited | where{$_.status -eq "suspended" -and $_.Queue -notlike "*\Submission*"} | Remove-Message -WithNDR $False

Exchange Server 2003 and Exchange 2000 Server

In Exchange 2003 or in Exchange 2000 Server, you can use MFCMapi tool to purge the messaging queues. For more information, see Microsoft Knowledge Base article 906557, How to use the Mfcmapi.exe utility to view and work with messages in the SMTP TempTables in Exchange 2000 Server and in Exchange Server 2003.

If there are many messages in the queue, you may want to limit how many messages are displayed at one time. To do this, follow these steps:

  1. In the MFCMapi tool, click Other on the toolbar.

  2. Click Options.

  3. Under Throttle Level, modify the value to an appropriate number of messages. For example, set the level to 1000.

Use a Transport Rule to Prevent Message Delivery

In Exchange 2010 and in Exchange 2007, you can use transport rules to inspect messages in the transport pipeline and take appropriate actions. For example, you can delete messages based on certain criteria. For more information, see Understanding Transport Rules.

You can use the New Transport Rule wizard from the Exchange Management Console to easily create a transport rule. To do this, follow these steps:

  1. Start the Exchange Management Console, and then locate and expand the Organization Configuration node.

  2. Click Hub Transport.

  3. In the Actions pane, click New Transport Rule.

  4. Follow the steps in the New Transport Rule Wizard to create the transport rule.

The following example commands show how to create a transport rule by using the Exchange Management Shell. The syntax for the commands differs slightly between Exchange 2010 and Exchange 2007. This is because the Exchange 2010 transport rule cmdlets have been simplified, letting you create or modify a transport rule by using a single command.

Exchange Server 2010 The following command creates a transport rule to delete any messages that contain “Surprise Party” in the Subject field:

New-TransportRule -Name "purge Surprise Party messages in Exchange 2010" -Priority '0' -Enabled $true -SubjectContainsWords 'Surprise Party' -DeleteMessage $true

Exchange Server 2007 The following command creates a transport rule to delete any messages that contain “Surprise Party” in the Subject field:

$condition = Get-TransportRulePredicate SubjectContains
 $condition.Words = @("Surprise Party")
 $action = Get-TransportRuleAction DeleteMessage
 New-TransportRule -name "purge Friday Party messages in Exchange 2007" -Conditions @($condition) -Actions @($action) -Priority 0

Note

If the Exchange organization contains Exchange 2010-based servers and Exchange 2007-based servers, you must create a transport rule for each Exchange version.

Conclusion

For more information about how to handle message removal in Exchange, see the following topics:

Microsoft Knowledge Base article 314327, How to Delete Messages from Queues in Exchange Server 2003

Understanding Mailbox Import and Export Requests

Additional Information