PowerShell: Search mailbox for items of a particular message class (ItemClass)

The Search-Mailbox cmdlet can be used to perform various searches within a mailbox, and while it serves many needs, there are some searches that it can’t do.  I wrote a PowerShell script to demonstrate how to perform a search of a mailbox (or mailboxes) using EWS, in this case looking for items of a particular message class.  The script will search through all the folders of the mailbox (from the Top of Information store) and log all items found to a text file.


The search can be modified quite easily by changing the parameters in the SearchFolder function (modify $searchFilter accordingly), and also any processing can be added to the ProcessItem function (this function is called for every item that matches the search – in the sample, this is the function that logs to the text file).

The EWS Managed API needs to be installed on the machine running the script, but it does not need to be run on an Exchange server.

Script updated 11/7/14 to add support for searching archive mailboxes, and some other minor code changes.



To list any items that match the message class:

.\Search-MailboxForMessageClass <mailbox smtp address> <message class>

To delete any found items:

.\Search-MailboxForMessageClass <mailbox smtp address> <message class> -DeleteItems



 -Mailbox: Specifies the mailbox to be accessed
 -MessageClass: Specifies the message class of the items being searched
 -SearchArchive: If this switch is specified, then the archive mailbox for the account (not the main mailbox) will be searched
 -DeleteItems: If this switch is specified, any items found will be deleted (use with care!!)
 -AuthUsername: Username used to authenticate with EWS (if omitted, the logged in user’s credentials are used)
 -AuthPassword: Password used to authenticate with EWS
 -AuthDomain: Domain used to authenticate with EWS
 -Impersonate: Whether we are using impersonation to access the mailbox (defaults to false)
 -EwsUrl: EWS Url (if omitted, then autodiscover is used)
 -EWSManagedApiPath: Path to managed API (if omitted, a search of standard paths is performed)
 -IgnoreSSLCertificate: Whether to ignore any SSL errors (defaults to false)
 -LogFile: Log file – activity is logged to this file if specified (if omitted, it is just written to console)


Comments (27)

  1. PSNewbie says:

    Hi Dave, reading through the script, i'm a bit lost at the main part where you can read in a csv file. how do you pass the csv file name to that parameter in the ps script listed under $FileExists = Test-Path $Mailbox? I'm having issues trying to use .search-mailboxformessageclass <filename.csv> <message class>

  2. PSNewbie says:

    ahh found the issue. The csv file was using the wrong name for the field name "SMTP" when generating the email address list. I changed the csv output to create the field name as PrimarySMTPAddress and the script now reads in the file.

  3. Jason says:

    Is there a way to add the folder structure to the output? Thanks for this great script!

  4. Normand Champagne says:

    Dave, the script does exactly what I'm looking for, but i would also like to be able to delete the messages. Can this been done with this script, or would addition code need to be added? If so, would you have that in another script?


  5. Jake says:

    Is it possible to modify the Message Class with Powershell?

  6. DonT says:

    I know this is an old thread but would you know how to modify the script to only count the number of items that are of a particular message class, all I need is quantity?


  7. The script could be modified to count.  Or you could dump the output into a spreadsheet and count that way (which means the script will work as is).

  8. Peter says:

    Have anybody tried to search with the impersonate option and a csv file:

    .search-mailboxformessageclass.ps1 -Mailbox test.csv -Impersonate

    I'm looking for any way to search more than one mailbox with the impersonate option?

  9. Sukhija Vikas says:

    Fantastic, this worked for us..

  10. Rajeev Verma says:

    Awesome script.. Thanks a lot..

  11. Danr says:

    Do you know a trigger to force deletion without prompting that the message has been unread?  I have been using this to delete EV vault stubs but some of the messages have not been read by the user.  This is causing exchange to create an additional email saying the message had been deleted without being read.  

    Please let me know when you can.

  12. I'm not aware of any functionality that would do this, unless the message sender requested a read receipt?  If they did, then you'd probably have to remove this flag from the message before deleting it.  Otherwise, as far as EWS is concerned, it doesn't matter if the message has been read or not – it will be deleted.

  13. Eric Pedersen says:

    Is there any way that this script could be modified to 'touch' the items belonging to a class such as IPM.StickyNote?  By touch, I mean update the timestamp like *nix does?  I'm fighting with the default archiving policy and no matter what I try, I can't stop our users' Notes folders from being archived.

  14. Navishkar Sadheo says:

    Hi, I keep getting this error:

    [PS] C:UsersnavishkarsDesktop>.Search-MailboxForMessageClass.ps1 -Mailbox "Navishkar.sadheo@ttaf.co.za"

    cmdlet Search-MailboxForMessageClass.ps1 at command pipeline position 1

    Supply values for the following parameters:

    (Type !? for Help.)

    MessageClass: IPM.Note.PamMessage

    Using managed API 15.00.0516.014 found at: C:Program FilesMicrosoftExchangeWeb Services2.0Microsoft.Exchange.WebServices.dll

    Using default credentials

    Mailbox = Navishkar.sadheo@ttaf.co.za

    Performing autodiscover for Navishkar.sadheo@ttaf.co.za

    EWS Url found:  webmail.ttaf.co.za/…/Exchange.asmx

    Exception calling "Bind" with "2" argument(s): "The specified object was not found in the store."

    At C:UsersnavishkarsDesktopSearch-MailboxForMessageClass.ps1:157 char:62

    +     $folder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind <<<< ($service,$FolderId)

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : DotNetMethodException

    Processing folder:

    Exception calling "FindItems" with "3" argument(s): "The specified object was not found in the store."

    At C:UsersnavishkarsDesktopSearch-MailboxForMessageClass.ps1:175 char:32

    +         $results = $service.FindItems <<<< ( $FolderId, $searchFilter, $view )

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : DotNetMethodException

    You cannot call a method on a null-valued expression.

    At C:UsersnavishkarsDesktopSearch-MailboxForMessageClass.ps1:188 char:44

    +     ForEach ($subFolder in $folder.FindFolders <<<< ($view))

       + CategoryInfo          : InvalidOperation: (FindFolders:String) [], RuntimeException

       + FullyQualifiedErrorId : InvokeMethodOnNull

    [PS] C:UsersnavishkarsDesktop>

  15. Rajeev verma says:

    BTW it is only removing 1000 messages from a folder at one time Can we increase the value to unlimited?

  16. rajeev verma says:

    BTW it removes 1000 messages at a time. Can the value be increased to unlimited?

  17. Milan Hira says:

    Is there any way I can also log the message ID of the email? Happy to get only the messageID if that's easier

  18. antar says:

    not running for me

    error to bind 2 parmaeters….

  19. Ravi says:

    The Date Time of the mail message isn't showing for me.

  20. Ravi says:

    I made this change under ProcessItem

    Log([string]::Format("{0} {1}", $item.DateTimeCreated, $item.Subject))

    And now I get the date and subject.

  21. Al says:

    Your script is awesome.  Thanks for creating it and posting it. I recently ran it again a mailbox that has been around for 15+ years.  I run this.

    .Search-MailboxForMessageClass.ps1 -mailbox:wally.poops@email.com -messageclass:ipm.noteenterprisevault.shortcut  -ewsmanagedapipath:d:exchange2013bin -deleteitems

    I get this error

    Add-Type : Cannot bind parameter 'Path' to the target. Exception setting "Path": "Cannot find path

    'D:exchange2013bin' because it does not exist."

    At D:PSTImportScriptsSearch-MailboxForMessageClass.ps1:75 char:19

    +             Add-Type -Path $EWSManagedApiPath

    +                            ~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : WriteError: (:) [Add-Type], ParameterBindingException

       + FullyQualifiedErrorId : ParameterBindingFailed,Microsoft.PowerShell.Commands.AddTypeCommand

    I have noticed that if the subject is blank I get this error

    Exception calling "Format" with "2" argument(s): "Value cannot be null.

    Parameter name: args"

    At D:PSTImportScriptsSearch-MailboxForMessageClass.ps1:103 char:3

    +         Log([string]::Format("Deleting item (Subject = {0})", $item.Subject))

    +         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

       + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

       + FullyQualifiedErrorId : ArgumentNullException

    After I removed the email with the blank subject the process ran clean and deleted all matching items

  22. Don't specify the -EwsManagedApiPath unless you have installed the EWS Managed API to an unusual location.  The script will find it in the standard paths.  I'll check out the issue with a blank subject – I thought I'd tested with that (and they should be ignored), but that may have been another script!

  23. Ramanand says:

    Anyone help me to get Office 365 supported script?

  24. Adam says:

    I am receiving the same "The specified object was not found in the store" error as  Navishkar.

    Navishkar, were you able to resolve this?


  25. Adam Miceli says:

    Please disregard; permissions issue.

  26. Charles says:


    I have had a few issues with the script that I’ve managed to overcome however now it recurses throughout the top of information store I get the following error under every folder:

    Exception calling “FindItems” with “3” argument(s): “An internal server error occurred. The operation failed.”
    At C:\temp\Search-MailboxForMessageClass.ps1:175 char:3
    + $results = $service.FindItems( $FolderId, $searchFilter, $view )
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ServiceResponseException

    I’ve tried to play around with permissions and edit the script but nothing seems to fix this issue, if this is still active can anyone help?



  27. Joel says:

    In order for this to work in Office 365 I had to:
    – Grant myself Full Permissions to the Mailbox
    – Grant myself Impersonation Rights in Office 365

    Then Run the Script Like this:
    .\Search-MailboxForMessageClass.ps1 -mailbox @ -messageclass IPM.Note.EnterpriseVault.Shortcut -ewsurl https://outlook.office365.com/EWS/Exchange.asmx -authusername @ -authpassword -authdomain -logfile C:\Path\logfile.log -Deleteitems