Removing Messages by Message Class

powershellLast version: 2.00, February 27th, 2021

Recently, I was asked if it is possible to remove stub items. The reason was they were going to transition to a newer version of Exchange and they wouldn’t be using the archiving solution in the new environment. When required, vendor tooling would be used to search through the existing archives.

In such cases it makes sense to remove the stubs from the mailbox, which are shortcut messages that points to a copy of the original message in the archive solution. The new environment won’t contain the required Outlook plugins or extensions to retrieve the original message from the archive using the stub, making the stub lead to a partial or empty message.

To identify stubs, one can filter on an attribute of each item, MessageClass. This attribute defines which kind of item it is (in fact, determines what form Outlook should use in order to present or process the information). Examples of MessageClass definitions are IPM.Note (regular e-mail messages), IPM.Note.EnterpriseVault.Shortcut (message archived by Enterprise Vault) or IPM.ixos-archive (message archived by Opentext/IXOS LiveLink E-Mail Archive).

To identify stubs from Outlook, add the Message Class field to your Outlook view, e.g.:

StubsOutlook

When you want to remove the stubs using Outlook, you can utilize the Advanced Find function of Outlook, but that is a very labor intensive, tedious and non-centralized per-mailbox procedure:

SearchFromOutlook

Requirements
Using the script requires Exchange Web Services (EWS) Managed API and for OAuth authentication the Microsoft Authentication Library (MSAL) libraries. You can install these packages from NuGet, or place their DLL’s in the same folder as the script. For an example of how to install EWS.Managed.Api from NuGet, see this article; for MSAL follow the same process but with the package titled ‘Microsoft.Identity.Client’.

Also take notice that since you’ll be processing user mailboxes, you’ll need to have full mailbox access or impersonation permissions when using Basic Authentication; the latter is preferred. For details on how to configure impersonation for Exchange On-Premises or Office 365, see this blog post. Using a registered app with OAuth is always through Impersonation.

Usage
The script Remove-MessagesClassItems.ps1 uses the following syntax:

Remove-MessageClassItems.ps1 [-Identity]  [-MessageClass] [-Type] [-Server ] [-Impersonation] [-DeleteMode ] [-Type] [-Before ] [-MailboxOnly] [-ArchiveOnly] [-IncludeFolders] -ExcludeFolders [-NoProgressBar] [-Force] [-WhatIf] [-Confirm] [-Secret] [-CertificateThumbprint] [-CertificateFile] [-CertificatePassword] [-TenantId] [-ClientId] [-TrustAll]

A quick walk-through on the parameters and switches:

  • Identity is the name or e-mail address of the mailbox.
  • MessageClass specifies the Message Class to remove, for example IPM.Note.EnterpriseVault.Shortcut (EnterpriseVault). You can use wildcards around or at the end to include folders containing or starting with this string, e.g. ‘IPM.ixos*’ or ‘*EnterpriseVault*’. Matching is always case-insensitive.
  • Server is the name of the Client Access Server to access for Exchange Web Services. When omitted, the script will attempt to use Autodiscover.
  • Type determines what folder class to process. Options are Mail, Calendar, Contacts, Tasks, Notes or All (Default).
  • Switch Impersonation specifies if impersonation will be used for mailbox access, otherwise the current user context will be used.
  • DeleteMode specifies how to remove messages. Possible values are HardDelete (permanently deleted), SoftDelete (use dumpster, default) or MoveToDeletedItems (move to Deleted Items folder). Note that the Deleted Items folder will be processed, unless MoveToDeletedItems is used.
  • Before can be used to only remove items received before specified date.
  • MailboxOnly specifies you only want to process the primary mailbox of specified users. You als need to use this parameter  when running against mailboxes on Exchange Server 2007.
  • ArchiveOnly specifies you only want to process personal archives of specified users.
  • IncludeFolders specifies one or more names of folder(s) to include, e.g. ‘Projects’. You can use wildcards around or at the end to include folders containing or starting with this string, e.g. ‘Projects*’ or ‘*Project*’. To match folders and subfolders, add a trailing \*, e.g. Projects\*. This will include folders named Projects and all subfolders. To match from the top of the structure, prepend using ‘\’. Matching is case-insensitive.
  • ExcludeFolders specifies one or more folder(s) to exclude. Usage of wildcards and well-known folders identical to IncludeFolders.
    Note that ExcludeFolders criteria overrule IncludeFolders when matching folders.
  • NoProgressBar prevents displaying a progress bar as folders and items are being processed.
  • ReplaceClass specifies that instead of removing the item, its PR_MESSAGE_CLASS class property will be modified to this value. For example, can be used in conjunction with MessageClass to modify any IPM.Note items pending Evault archival back to regular items, using: -MessageClass IPM.Note.EnterpriseVault.PendingArchive -ReplaceClass IPM.Note
  • Report reports individual items detected as duplicate. Can be used together with WhatIf to perform pre-analysis.
  • TrustAll can be used to accept all certificates, e.g. self-signed certificates or when accessing Exchange using endpoint with a different certificate.

For authentication, the following parameters are available:

  • Credentials specifies credentials to use for Basic Authentication.
  • TenantId specifies the identity of the Tenant (OAuth)
  • ClientId specifies the Id of the registered application (OAuth).
  • CertificateThumbprint specifies the thumbprint of the certificate from personal store to use for authentication (OAuth).
  • CertificateFile specifies the external certificate file (pfx) to use for authentication (OAuth). This certificate needs to contain a private key; the registered application needs to contain the certificate’s public key.
  • CertificatePassword optionally specifies the password to use with the certificate file (OAuth).
  • Secret specifies the secret to use with the application (OAuth).

Well-Known Folders
For IncludeFolders, ExcludeFolders, you can also use well-known folders using this format: #WellKnownFolderName#, e.g. #Inbox#. Supported are #Calendar#, #Contacts#, #Inbox#, #Notes#, #SentItems#, #Tasks#, #JunkEmail# and #DeletedItems#. The script uses the currently configured Well-Known Folder of the mailbox to be processed.

Patterns
Here are some examples of using pattern matching in IncludeFolders or ExcludeFolders, based on the following tree structure:

+ TopFolderA
  + FolderA
    + SubFolderA
    + SubFolderB
  + FolderB
+ TopFolderB

The following filters will match folders from the above structure:

Filter Matches
FolderA \TopFolderA\FolderA, \TopFolderB\FolderA
Folder* \TopFolderA\FolderA, \TopFolderA\FolderB, \TopFolderA\FolderA\SubFolderA, \TopFolderA\FolderA\SubFolderB
FolderA\*Folder* \TopFolderA\FolderA\SubFolderA, \TopFolderA\FolderA\SubFolderB
\*FolderA\* \TopFolderA, \TopFolderA\FolderA, \TopFolderA\FolderB, \TopFolderA\FolderA\SubFolderA, \TopFolderA\FolderA\SubFolderB, \TopFolderB\FolderA
\*\FolderA \TopFolderA\FolderA, \TopFolderB\FolderA

Example
Suppose you want to remove  IPM.Note.EnterpriseVault.Shortcut items from the mailbox of user1 and personal archive when enabled, moving the items to the DeletedItems by Impersonation. In such case, you could use the following cmdlet:

Remove-MessageClassItems.ps1 -Identity user1 -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode MoveToDeletedItems -Impersonation –Verbose 

Note: Screenshot shows Mailbox parameter, which is per 1.52 renamed to Identity

SampleOutput

Note: By default, Remove-MessageClassItems will only search IPF.Note class folders (i.e. containing mail items), so you’ll only see those being processed. If you want all folders scanned (also classless), use the ScanAllFolders switch.

The script also supports Office 365. For example, to remove all items with ‘Enterprise’ in their message class text, received before 1/1/2014, only from the primary mailbox, excluding the folder Personal, using Basic Authentication, you can use:

$Credentials= Get-Credential
Remove-MessageClassItems.ps1 -Identity olrik@office365tenant.com -DeleteMode MoveToDeletedItems -MessageClass *EnterpriseVault* -Before 1/1/2014 -MailboxOnly -ExcludeFolder Personal -Credentials $Credentials

In case you want to process multiple mailboxes, you can use a CSV file which needs to contain the Identity field. An example of how the CSV could look:

Identity
francis
philip

The cmdlet could then be something like:

Import-CSV users.csv1 | Remove-MessageClassItems.ps1 -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDelete 

Feedback
You’re feedback is welcomed through the comments; if you got scripting suggestions, please use the contact form.

Download
You can download the script from the GitHub here.

275 thoughts on “Removing Messages by Message Class

  1. Pingback: Removing Messages by Message Class (Updated) | EighTwOne (821) | JC's Blog-O-Gibberish

  2. Michel, I would appreciate to learn more about the migration from EV to Exchange archiving. We are planning on moving from an Exchange 2007/EV 9.0.2 implementation to Exchange 2013 and the road block is the 34TB of data in EV and how best to get it back to exchange. On the plus side, we are not stubbing our emails.

    Like

    • Transvault 🙂
      Nb : when you say 34TB is y compressed and duplicated DATA (singleinstanceless) ? Or just the DATA in disk ?
      Regards,
      Ben

      Like

    • Depends on scenario and setup. This customer cut off their archived data (ie isolated, available when required using vendor tools) and will move not (yet) archived mailbox data to the new e-mail environment, facilitating large mailboxes. Otherwise, they’d have to inject the original mail items using the stubs from the archive, but as that data is rarely consulted but has a significant storage footprint, they chose to cut it off.

      Like

  3. Fantastic script! This is extremely useful in removing third-party voice mail messages from the Exchange environment. Not being very adept at EWS scripitng(but learning); I’d like to know how would the script be modified to find messages of a specific class older than, let’s say, 45-days?
    (define in base property set and then filter?)

    Like

    • The line with “[Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, $MessageClass)” defines the item filter. It’s a single condition; if you want to combine multiple conditions, you need to utilize a SearchFilterCollection where you add all the individual conditions. Then, pass that collection instead of the single condition to FindItems, e.g.

      ..
      $ItemSearchFilter1= New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo( [Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, $MessageClass)
      $ItemSearchFilter2= New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo( [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, (Get-Date).AddDays(-45))
      $SearchFilterCollection= New Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
      $SearchFilterCollection.add( $ItemSearchFilter1)
      $SearchFilterCollection.add( $ItemSearchFilter2)

      Do {
      $ItemSearchResults= $SubFolder.FindItems( $ItemSearchFilterCollection, $ItemView)
      ..

      Let me know if there’s more animo for this; I could make item age a parameter of course 🙂

      Like

      • Thank you for the prompt reply. When I replace the code to create the SearchFilterCollection..I get an error regarding “New”…seems to run, then remove all messages, not just message class specified. (Of course, working in a test environment)
        [PS] E:\hold\msgclass>.\Remove-MessageClassItems45days.ps1 -Mailbox mbxrestore -MessageClass IPM.Note.Adomo.VM -DeleteMode MoveToDeletedItems -Impersonation -Verbose
        Processing mailbox mbxrestore
        VERBOSE: Loading Microsoft.Exchange.WebServices.dll
        VERBOSE: Set to trust all certificates
        VERBOSE: Using MBX.Restore@xxxxxx.xxxfor impersonation
        VERBOSE: Looking up EWS URL using Autodiscover for MBX.Restore@xxxxxx.xxx
        VERBOSE: Using EWS on CAS https://owa.xxxxxx.xxx/EWS/Exchange.asmx
        VERBOSE: DeleteMode is MoveToDeletedItems
        VERBOSE: Removing messages of class IPM.Note.Adomo.VM
        VERBOSE: Processing folder Drafts
        The term ‘New’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spellin
        g of the name, or if a path was included, verify that the path is correct and try again.
        At E:\hold\msgclass\Remove-MessageClassItems45days.ps1:166 char:38
        + $SearchFilterCollection= New <<<< Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollec
        tion([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
        + CategoryInfo : ObjectNotFound: (New:String) [], CommandNotFoundException
        + FullyQualifiedErrorId : CommandNotFoundException

        You cannot call a method on a null-valued expression.
        At E:\hold\msgclass\Remove-MessageClassItems45days.ps1:167 char:35
        + $SearchFilterCollection.add <<<< ( $ItemSearchFilter1)
        + CategoryInfo : InvalidOperation: (add:String) [], RuntimeException
        + FullyQualifiedErrorId : InvokeMethodOnNull

        Like

        • Change:

          $ItemSearchFilter= New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo( [Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, $MessageClass)

          Do {
          $ItemSearchResults= $SubFolder.FindItems( $ItemSearchFilter, $ItemView)

          to:

          $ItemSearchFilter1= New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo( [Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, $MessageClass)
          $ItemSearchFilter2= New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThanOrEqualTo( [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, (Get-Date).AddDays(-45))
          $ItemSearchFilterCollection= New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::And);
          $ItemSearchFilterCollection.add( $ItemSearchFilter1)
          $ItemSearchFilterCollection.add( $ItemSearchFilter2)

          Do {
          $ItemSearchResults= $SubFolder.FindItems( $ItemSearchFilterCollection, $ItemView)

          Like

  4. Pingback: The UC Architects » Episode 22: A Game of Clouds

  5. Fantastic !! Can’t thank you enough. Is it possible to make age as one of the parameters and also parameter to include a specific folder.

    Regards.

    Like

  6. Thanks for the script. I am going to try to use it to see if my novice PS skills can change it from deleting items with a specified message class to changing them to a similiar but different message class. If you happen to catch this and have suggestions they would be appreciated.

    Thanks again for your contributions to date.

    J

    Like

  7. I found a similar solution using the search-mailbox command. I chose this meathod because I’m not using Exchange Web Services.

    Search-Mailbox -Identity $UserName -SearchQuery “IPM.NOTE.EnterpriseVault.Shortcut” -DeleteContent

    You can test with the following commands.

    This produces a report with the number of hits
    Search-Mailbox -Identity $UserName -SearchQuery “IPM.NOTE.EnterpriseVault.Shortcut” –EstimateResultOnly

    This will search a mailbox, and all hits will be copied to another mailbox. You can look at that mailbox and see exactly what would be deleted if you used the -DeleteContent command.

    Search-Mailbox -Identity $UserName -SearchQuery “IPM.NOTE.EnterpriseVault.Shortcut” -TargetMailbox “TestMB” -TargetFolder “$UserName”

    Like

    • Mike,

      sorry to latch on so late, but i’ve tried doing the search way (because i thought it would be faster), but the content detected doesn’t seem to catch everything. I’d like to use search because it’s faster.

      Thoughts?

      Like

  8. Just tried this script today but unfortunately it’s not working for me. In verbose mode I can see it’s processing all the folders, but it didn’t find any e-mails with the class “IPM.Note.EnterpriseVault.Shortcut”.

    Like

      • After reading the comment below, I know why it’s not working for me. The stub mail items are all in the archive mailbox. Because the script did show all folders including the ones in the archive mailbox I thought it was also working for the archive. I will use the search-mailbox cmdlet for now. If you include the option to process the archive it will be a great script. Keep up the good work!

        Like

  9. This is an awesome script. Thank you so much for taking the time to publish it.

    What if the stub is already in the user’s Personal Archive? Can we use the script to remove stubs that have already been moved to the user’s archive?

    Like

  10. Hello Michel,

    when I run the script I get the following. I’m running the script as a Exchange Administrator and I have full mailbox rights on this test account. I doesn’t remove any of the EAS messageClass items.

    [PS] C:\>.\Remove-MessageClassItems.ps1 -Mailbox Training6 -DeleteMode SoftDelete -MessageClass IPM.Note.EAS -ScanAllFolders -verbose
    Processing mailbox Training6
    VERBOSE: Loading Microsoft.Exchange.WebServices.dll
    VERBOSE: Set to trust all certificates
    VERBOSE: Looking up EWS URL using Autodiscover for Training6@****.com
    VERBOSE: Using EWS on CAS https://mail.****.com/EWS/Exchange.asmx
    VERBOSE: DeleteMode is SoftDelete
    VERBOSE: Removing messages of class IPM.Note.EAS
    VERBOSE: Scanning all folders
    VERBOSE: Processing folder Calendar
    VERBOSE: Processing folder Contacts
    VERBOSE: Processing folder Conversation Action Settings
    VERBOSE: Processing folder Drafts
    VERBOSE: Processing folder Inbox
    VERBOSE: Processing folder Journal
    VERBOSE: Processing folder Junk E-Mail
    VERBOSE: Processing folder Notes
    VERBOSE: Processing folder Outbox
    VERBOSE: Processing folder Quick Step Settings
    VERBOSE: Processing folder RSS Feeds
    VERBOSE: Processing folder Sent Items
    VERBOSE: Processing folder Suggested Contacts
    VERBOSE: Processing folder Sync Issues
    VERBOSE: Processing folder Conflicts
    VERBOSE: Processing folder Local Failures
    VERBOSE: Processing folder Server Failures
    VERBOSE: Processing folder Tasks

    Like

  11. Great script. Thanks for it. I have a few suggestions that you can take or leave.

    Enhancement Suggestions:
    – Include a parameter to specify the EWS API location of not installed to the default location
    – Include a parameter to run as a desired account to impersonate.
    -Include a parameter to change the default EWS Exchange2007_SP1 config to other versions of Exchange
    + I did this as part of Troubleshooting my issues and it appeared to not be the root cause of the issue. Perhaps this is not required because of it.
    – Provide a parameter to output verbose output to a log file (I know we can pipe it… but it would be nice to have on screen and off screen output)

    Thanks again for the hard work and responsive assistance

    Like

  12. Hi,

    I’ve managed to get the script to run without error now (had various syntax issues), but it doesn’t seem to be doing anything. After hitting enter, I just get a line showing ‘>>’ and no further action happens.

    I am trying to permanently remove EV stubs in Exchange 2010:

    .\Remove-MessageClassItems.ps1″ -Mailbox TomU -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDelete -Impersonation –Verbose

    Any ideas please?
    Thanks

    Like

    • You shouldn’t have to edit the script. If you copied/pasted the exact cmdlet, there’s a quote mismatch (the >> means its waiting for more input since you didn’t enter a closing quote). Try this:
      .\Remove-MessageClassItems.ps1 -Mailbox TomU -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDelete -Impersonation –Verbose

      Like

      • My bad, I didn’t edit the script, just the cmdlet. Your amendment now generates an error telling me it is not digitally signed? Appreciate your help.

        File C:\2010\Remove-MessageClassItems.ps1 cannot be loaded. The file C:\2010\Remove-MessageClassItems.ps1 is not digitally signed. The script will not execute on the system. Please see “get-help about_signing” for more details..
        At line:1 char:31
        + .\Remove-MessageClassItems.ps1 <<<< -Mailbox TomU -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDe
        lete -Impersonation -Verbose
        + CategoryInfo : NotSpecified: (:) [], PSSecurityException
        + FullyQualifiedErrorId : RuntimeException

        Like

          • Thanks, getting there…. A whole host of errors now though. Appreciate your time but understand if you have better things to be doing!

            Processing mailbox TomU
            VERBOSE: Loading Microsoft.Exchange.WebServices.dll
            Exception calling “LoadFile” with “1” argument(s): “The system cannot find the file specified. (Exception from HRESULT:
            0x80070002)”
            At C:\2010\Remove-MessageClassItems.ps1:253 char:46
            + [void][Reflection.Assembly]::LoadFile <<<< ( $EwsDll)
            + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
            + FullyQualifiedErrorId : DotNetMethodException

            Unable to find type [Microsoft.Exchange.WebServices.Data.ExchangeVersion]: make sure that the assembly containing this
            type is loaded.
            At C:\2010\Remove-MessageClassItems.ps1:257 char:80
            + $ExchangeVersion= [Microsoft.Exchange.WebServices.Data.ExchangeVersion] <<<< ::Exchange2007_SP1
            + CategoryInfo : InvalidOperation: (Microsoft.Excha…ExchangeVersion:String) [], RuntimeException
            + FullyQualifiedErrorId : TypeNotFound

            New-Object : Cannot find type [Microsoft.Exchange.WebServices.Data.ExchangeService]: make sure the assembly containing
            this type is loaded.
            At C:\2010\Remove-MessageClassItems.ps1:258 char:32
            + $EwsService= New-Object <<<< Microsoft.Exchange.WebServices.Data.ExchangeService( $ExchangeVersion)
            + CategoryInfo : InvalidType: (:) [New-Object], PSArgumentException
            + FullyQualifiedErrorId : TypeNotFound,Microsoft.PowerShell.Commands.NewObjectCommand

            Property 'UseDefaultCredentials' cannot be found on this object; make sure it exists and is settable.
            At C:\2010\Remove-MessageClassItems.ps1:259 char:21
            + $EwsService. <<<< UseDefaultCredentials= $true
            + CategoryInfo : InvalidOperation: (UseDefaultCredentials:String) [], RuntimeException
            + FullyQualifiedErrorId : PropertyNotFound

            VERBOSE: Set to trust all certificates
            VERBOSE: Using TomU@contoso.com for impersonation
            Unable to find type [Microsoft.Exchange.WebServices.Data.ConnectingIdType]: make sure that the assembly containing this
            type is loaded.
            At C:\2010\Remove-MessageClassItems.ps1:270 char:165
            + $EwsService.ImpersonatedUserId= New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Micros
            oft.Exchange.WebServices.Data.ConnectingIdType] <<<< ::SmtpAddress, $EmailAddress)
            + CategoryInfo : InvalidOperation: (Microsoft.Excha…onnectingIdType:String) [], RuntimeException
            + FullyQualifiedErrorId : TypeNotFound

            VERBOSE: Looking up EWS URL using Autodiscover for TomU@contoso.com
            Write-Error : A positional parameter cannot be found that accepts argument 'You cannot call a method on a null-valued e
            xpression.'.
            At C:\2010\Remove-MessageClassItems.ps1:286 char:28
            + Write-Error <<<< "Autodiscover failed: " $error[0]
            + CategoryInfo : InvalidArgument: (:) [Write-Error], ParentContainsErrorRecordException
            + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WriteErrorCommand

            Like

          • @Tom: “Using this script requires Exchange 2007 or later and Exchange Web Services Managed API 1.2 (or later) which you can download here or you can copy the Microsoft.Exchange.WebServices.DLL locally and adjust the DLL path mentioned in the script when necessary. “

            Like

          • @No need to install; you can install it elsewhere, copy the Microsoft.Exchange.WebServices.DLL in the same location as the script and change the location (in the script) accordingly. Updated version of script will have additional logic to look for that DLL by the way.

            Like

          • Me again…. Now I’ve got this far… “Can’t access mailbox information store” ?

            Cheers

            Processing mailbox TomU
            VERBOSE: Loading Microsoft.Exchange.WebServices.dll
            VERBOSE: Set to trust all certificates
            VERBOSE: Using TomU@square-enix.com for impersonation
            VERBOSE: Looking up EWS URL using Autodiscover for TomU@xxx
            VERBOSE: Using EWS on CAS https://xxx/EWS/Exchange.asmx
            C:\2010\Remove-MessageClassItems.ps1 : Can’t access mailbox information store
            At line:1 char:31
            + .\Remove-MessageClassItems.ps1 <<<< -Mailbox TomU -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDe
            lete -Impersonation -Verbose
            + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
            + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Remove-MessageClassItems.ps1

            Like

  13. Is it possible to target only one folder? For instance I want to delete the
    IPM.Note.Microsoft.Conversation files from the ConversationHistory folder on 139 mailboxes. I have my csv file ready but I was hoping to avoid running the script against all of the folders in each mailbox.

    Thanks!

    Like

  14. Hi,

    I wanted to know whether this script will work on a 2013 exchange evironment? i have followed the directions posted in reference to impersonation, but still running into errors whether i excute the script from a CAS or MBX server. I am trying to delete stubs from a legacy email archive solution.

    Thanks!

    Like

  15. Thanks for this script.. I’m trying to purge EV Shortcuts from a broken EV environment before a mail migration to O365. Script works fine for the most part but for a few users I get the “Can’t access mailbox information store” at line 1 char:31. These are users that are on the exact same mailbox database that users that the script works fine for. Impersonation is set on the account I’m using to run the script and again seems to work for the other users. Size issue? (these are fairly large Exchange 2010 mailboxes) I went so far as to specifically add Full right and send as to these problem mailboxes with no luck

    Like

      • Figured it out.. I did a

        get-mailbox -identity “username” | export-csv user.csv -notype

        On the users that were not working and discovered that despite showing as a regular User Mailbox in Exchange 2010 Management Console these mailboxes were apparently Linked Mailboxes. I discovered this by comparing all the properties of the mailboxes that did not work vs the ones that did and discovered that the failing mailboxes had a Linked Maser Account value (I have a very old Exchange environment that originally started as Exchange 5.5 so somewhere along the way during the migrations some mailboxes came across as Linked)

        So I ran

        Set-User -Identity user@fabrikam.com -LinkedMasterAccount $null

        against the failing mailboxes and now the script is working.

        Like

  16. Will this work against office 365. I have tried several times without luck. Getting the following error.

    C:\Scripts\Remove-MessageClassItems.ps1 : Can’t access mailbox information store

    Like

  17. Pingback: Script Updates | EighTwOne (821)

  18. Hi,

    I’m running the script and it seems to be working great, (both against on prem and cloud users), however on certain users it just stalls out on various folders. They don’t seem to be that large but they just can’t process past them. I’ve run it in verbose but i don’t see anything specific being reported.

    Like

  19. I dont think the “before” switch is working, or I am using the wrong format. What format does the -before switch need. Does it need the date that you want all items older than that to be deleted, or is it the number of days before. If it is date, what format do you need date? I tried MM/DD/YYYY and YYYY/MM/DD and it does not work properly with any of those. Or at least it’s not parsing it correctly. See the results below. Using the -before, it is still trying to remove ALL my stubs, not just the ones before the date I have specified. I am running it against an Exchange 2007 SP3 environment in preparation for moving to Exchange 2013 SP1

    [PS] C:\Download>.\Remove-MessageClassItems.ps1 -Mailbox %USERNAME% -MessageClass IPM.Note.MimosaStubmimosa1 -Server cas2.MYDOMAIN.COM -DeleteMode har
    ddelete -Before 2013/06/01 -Verbose
    VERBOSE: Loading C:\Program Files\Microsoft\Exchange\Web Services\2.0\\Microsoft.Exchange.WebServices.dll
    Processing mailbox USERNAME (%USERNAME%@MYDOMAIN.COM)
    VERBOSE: Set to trust all certificates
    VERBOSE: Using Exchange Web Services URL https://cas2.MYDOMAIN.COM/EWS/Exchange.asmx
    VERBOSE: DeleteMode is harddelete
    VERBOSE: PartialMatching is False
    VERBOSE: Removing messages of class IPM.Note.MimosaStubmimosa1
    VERBOSE: Removing messages older than 06/01/2013 00:00:00
    VERBOSE: Scanning all IPF.Note (E-mail) folders
    VERBOSE: Processing folder Deleted Items
    VERBOSE: Processing folder Drafts
    VERBOSE: Processing folder Inbox

    Confirm
    Are you sure you want to perform this action?
    Performing operation “Remove-MessageClassItems.ps1” on Target “Remove 431 items from Inbox”.
    [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is “Y”):

    Like

    • The format provided should be in the current locale, e.g. mm/dd/yyyy (it’s of type datetime so you could fit in a time as well). This is evaluated against the item’s Received attribute. To see the expected format, use:
      (Get-Culture).dateTimeFormat | Select *Pattern
      (don’t have Ex2007 at hand, should this be a PoSH 1 quirk, you can run it from system running a more recent version of PoSH – it will run against your mailbox on Ex2007SP1+).

      Like

      • I ended up finding out it was an issue with impersonation. When I use impersonation it works fine. I also ran it from a 2012 server running Powershell 3 and the Exchange 2013 tools on it. One additional thing, when it goes to delete the items, it always prompts you to confirm. This is a good thing by and large. But it seems for whatever reason when it calculates the number of items in the inbox, it is more than double what the actual stubbed email count is. Every other folder it seems to be 100% accurate. Can you think of a reason for this, or perhaps it might be a quirk?
        At any rate, I have to compliment you on this and a couple other powershell scripts I have found on your site (the delete duplicate items one), they have saved me a ton of hassle. I was planning on just using managed default folder policy to just delete all emails older than a certain thresshold just to purge the stubs, but this is much more palatable to the end user…

        Like

      • Michael, I found another thing I cannot explain. When running the script, it appears to not parse certain mail folders inside certain users mailboxes at all. Nothing special about these folders. I thought it might be a permissions thing, but looking at folder permissions, it seems that there would be nothing inhibiting it. Plus I thought with Impersonation, folder permission should be irrelavent. I could look at something in MFCMapi or PFDavAdmin if you want, but I am not sure what to look for. I did verify the messageclass is identical across all instances, (IPM.Note.MimosaStubMimosa1), so it’s not like some stubs have different message class than others. Every test user I have run this against has similar results. It’s just not hitting all folders.

        Like

      • Michael, I think I may have found my problem, let me know if you think. When looking at the script run, I found this text when running in verbose mode: “VERBOSE: Scanning all IPF.Note (E-mail) folders”. This led me to go into MFCMapi and look at the folders not being parsed. It appears none of these folders have the “PR_CONTAINER_CLASS” of IPF.Note. In fact, the folders do not have even have the “PR_CONTAINER_CLASS” field at all. Not just not poplulated, they do not have that attribute at all. For your information, about 80% of my users were migrated to Exchange from Groupwise many years ago, and it’s possible that when these folders were created in Exchange at that time by the (Quest) Migration tool, it created the folders as some odd type? Just throwing this out there. You may or may not have a way to fix this… thanks again.

        Like

    • All roads lead to Rome. Yet, that requires Ex2010 or up, EMS or being member of the Discovery Management role group to name a few. There are lots of pre-Ex2010 customers that want to get rid of the stubs before moving data over (On-Prem or ExO). The script is not meant to be a replacement for Search-Mailbox (as indicated in another comment).

      Like

    • The search-mailbox command does not exist in Exchange 2007. Just a heads up that there are thousands of customers out there still using Exchange older than 2010. Sometimes Microsoft forgets that. We cant all be bleeding edge.

      Like

  20. For those wanting to actually bring their Enterprise Vault archived data to their new environment (Exchange 2010, Exchange 2013, Office 364 or Google Apps for Business), our Archive Accelerator Restore product can do that. (link removed)
    Dave

    Like

  21. Pingback: Remove EnterPriseVault Shortcut Class Message after Office 365 Migration | Microsoft Technologies Blog

  22. Hi Michel,

    Thanks for the script!
    I was using search-mailboxformessageclass.ps1 first, but this one seems easier to use. (especially that you can use the partial parameter)

    I do have a problem however that no emails are found.
    When I run it with the other powershell, it does find results.

    I’m looking in the correct mailbox (not archive)

    Like

  23. I Love the script ! Its saved no end of time and effort, its very easy to use and does exactly what it says,

    What it does do though, is if a read reciept had been archived and it removed the reciept stub, Exchange sends an email to sender of message stating that the item was deleted and not read.

    Is there anyway to prevent this happening ?

    This will no doubt generate quite a few bits of confusion on mass mailbox enviroments.

    Like

  24. Any other options for processing against multiple mailboxes?
    I’ve tried the imiport-csv option but I get an error that The term ‘Remove-MessageClassItems.ps1’ is not recognized as the name of a cmdlet. I would like to be able to run this on a per database level.

    get-mailboxdatabase | get-mailbox | The term ‘Remove-MessageClassItems.ps1

    Like

  25. Getting issue as below…..
    C:\Remove-MessageClassItems.ps1 : Can’t access mailbox information store
    At line:1 char:1
    + .\MessageClass1.ps1 -Mailbox Test1 -MessageClass IPM.Note.Aftermail …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Remove-MessageClassItems.ps1

    Like

  26. Does the script work against Office 365 mailboxes? I have ~200 mailboxes on Office 365 and I get below error when I run the script:
    C:\Scripts\Remove-MessageClassItems.ps1 : A positional parameter cannot be found that accepts argument ‘Exception
    calling “AutodiscoverUrl” with “2” argument(s): “The Autodiscover service couldn’t be located.”‘.
    At line:1 char:1
    + .\Remove-MessageClassItems.ps1 -Mailbox rafatsum@genedemo.onmicrosoft.com -Messa …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Remove-MessageClassItems.ps1], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Remove-MessageClassItems.ps1

    Like

    • As already answered through e-mail, check your Autodiscover configuration, DNS entries, firewall settings etc.
      You could bypass Autodiscover process directly specifying the EWS endpoint (using Server parameter), e.g. outlook.office365.com
      Are you using Hybrid configuration?

      Like

  27. Yes this is a Hybrid configuration. As a matter of fact, i have also tried a pure Office 365 tenant, from different location and got excat same error.
    I have used the -server switch with Outlook.Office365.com and got “can’t access mailbox information store” error.

    In both cases, i have given a user account that has tenant global admin, Exchange Org Managrnt and impersonation rights (I followed the instructions and assigned the right using the GUI)
    ??
    Thanks for your help
    Rafat

    Like

  28. Thanks for the script. It ran successfully however as it ran, I received couple undelivered messages which I did not send and also don’t have on my sent items. I tested with another user got the same issue. Any idea, could it be read receipt of unread emails?

    Like

  29. Can you provide me a resource as to make this script work. I have no knowledge of scripting with EWS. I have installed the API but all attempts to make it work have failed.
    Thanks
    Jamie

    Like

  30. Script runs but doesn’t delete anything. I have tried exact message class name and partialmatching switch with general class name and neither will delete the items from my deleted items.

    Like

  31. Folder permissions from Outlook are Default and Anon. – None. Shouldn’t I get an error if I don’t have rights? I will read more into the impersonation.

    Like

  32. Hi Michael,

    Thanks for the script. it is working fantastically for most of our mailboxes and folders within it. we are triggering the script from Exchange 2010 and we have exchange 2010 as well as exchange 2013 in our environment.

    We have processed most of the mailboxes and folders within it but suddenly for one of the folder of anyone mailbox we faced following error as shown below. we tried accessing this folder and we could access it. the common thing is that we are facing issue only with mailboxes located in exchange 2013. we have verified personification permission and are good on that side for user credentials being used. also, it is working for all folders except few from exchange 2013. request you to kindly look into it.

    PS>TerminatingError(): “Exception calling “FindItems” with “2” argument(s): “The request failed. The operation has timed out””
    C:\do not delete\remove\Remove-MessageClassItems.ps1 : Can’t access mailbox
    information store

    Like

    • Default timeout for EWS operations is 100 sec. Extend that period inserting the following line below “$EwsService.UseDefaultCredentials= $true”:
      $EwsService.Timeout= X
      where X is the timeout in milliseconds
      A ‘timeout’ parameter is on the wishlist.

      Like

      • hi Michael,

        Thanks for the suggestions. However, we are facing another issue with the same script. for some folders it prompts the previous error and for some the one that is displayed below.
        I verified on permission side and also tried accessing those folders by logging to users profile by admin account, it is accessible in that way. Any kind of help or suggestions would be helpful. also, any link or blog with in depth information about EWS operations and flow and specifically in regards to this script would be of a great help to us.

        C:\do not delete\remove\Remove-MessageClassItems.ps1 : Can’t access mailbox information store
        At D:\Messaging_ShortcutReport\shrtct_rpt_ForTesting.ps1:9 char:26
        + Import-CSV .\users.csv | .\Remove-MessageClassItems.ps1 -MessageClass IPM.Note.E …
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
        + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Remove-MessageClassItems.ps1

        Transcript stopped, output file is C:\Do not delete\remove\ShrtctCounts-09-04-2015.txt

        Regards,

        Rohit M. Dhole

        Like

        • just to add that it processed most of the folders very well in this mailbox. However, failed for a specific folder which is different than the todays folder.

          Regards,

          Rohit M. Dhole

          Like

            • Yes, permissions are very much in order and so it is working fine with other users and folder on the same server. However, if you want me to verify some permissions please let me know so that I could verify and get back with result.

              Like

              • Hi Michael,

                At the beginning of the script I saw another error message, don’t know if it has anything to do with the error. I have given that error below which is related to EWSManagedAPI.

                WARNING: Active Directory server settings remained unchanged.
                Load-EWSManagedAPIDLL : The term ‘Load-EWSManagedAPIDLL’ is not recognized as the name of a cmdlet, function, script
                file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct
                and try again.
                At C:\do not delete\remove\Remove-MessageClassItems.ps1:341 char:5
                + Load-EWSManagedAPIDLL
                + ~~~~~~~~~~~~~~~~~~~~~
                + CategoryInfo : ObjectNotFound: (Load-EWSManagedAPIDLL:String) [], CommandNotFoundException
                + FullyQualifiedErrorId : CommandNotFoundException

                Regards,

                Rohit M. Dhole

                Like

                • After making changes in EWS time out as suggested by you, here is the next error.

                  PS>TerminatingError(): “Exception calling “FindItems” with “2” argument(s): “An internal server error occurred. Try again later.””
                  C:\do not delete\remove\Remove-MessageClassItems.ps1 : Can’t access mailbox information store

                  Any help or any pointer would really be of great help !

                  Regards,

                  Rohit M. Dhole

                  Like

      • Hi Michel,

        Thanks a ton for your solution. I believe the first solution for Time Out value worked for me, I am able to complete the reports fluently. it gave above errors and I just increased the Time out value and it started working.

        I am further looking for a script which could help me to remove older data which is older than specific date in a Mailbox.. it would be great if you could guide me to any script which could help me in this case.

        Also, tough the reports are successful. I am getting this error at the beginning of all users information. do we have any workaround to overcome it ?

        $EwsService.Timeout= X
        where X is the timeout in milliseconds
        A ‘timeout’ parameter is on the wishlist.

        Regards,

        Rohit M. Dhole

        Like

          • Hi Michel,

            Yes, it is the one time task and need to do on multiple users at a time. so just for one time activity we can’t go ahead and have retention tags and policies for multiple users.

            Also, I pasted the error that I am getting in current script. Thanks for looking into it.

            Regards,

            Rohit M. Dhole

            Like

  33. I believe I pasted wrong error,

    Load-EWSManagedAPIDLL : The term ‘Load-EWSManagedAPIDLL’ is not recognized as
    the name of a cmdlet, function, script file, or operable program. Check the
    spelling of the name, or if a path was included, verify that the path is
    correct and try again.

    Regards,

    Rohit M. Dhole

    Like

  34. Hello Michael,

    I am wondering when the messages are being deleted from the mailbox, if there is a way to suppress responses to and silently delete delivery receipts/read receipts. I am finding when we remove these messages for any archives messages that had any of these requests tagged a lot of auto responses get generated like “not read: re..” etc

    Thanks,
    J-

    Like

  35. Hi Michel, great script! I am trying to use it to delete stubs from Enterprise Vault old than xx/xx/xxxx and it seems to be working as far as scanning the mailbox, etc. However, no items get deleted as it finds none. I notice that it is not scanning a folder that our PST migration process creates in the mailbox called _Personal Archive. This folder is created directly under the user mailbox such as:
    >Joe.Smith@contoso.com
    >Inbox
    >_Personal Archive

    I also tried -ScanAllFolders with no luck.

    Am I missing something as to why it wont scan this particular folder? I read through entire blog and comments and didn’t see anything jump out.

    Thanks for any guidance you can provide!!!

    Like

  36. Hi Michel,
    we are doing the same migration but i can’t delete anything for now… i need to create a search folder in everyone’s outlook with all the message Class IPM.NOTE.EnterpriseVault.Shortcut in it so the user can clean their own vault.
    can you help me please?

    Like

  37. Hi:

    I’m trying to execute this command against a specific database and get an error. The command is

    Get-MailboxDatabase DB_xxx | Get-Mailbox | .\Remove-MessageClassItems.ps1 -Impersonation -Verbose -DeleteMode SoftDelete -MessageClass EnterpriseV
    ault -PartialMatching -ScanAllFolders -Before ((Get-Date).AddDays(-730))

    The error is:
    The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.At line:1 char:79

    Any ideas?

    Thanks

    Patxi

    Like

    • The name of the mailbox parameter is mailbox, and you’re not passing that one via Get-Mailbox. Instead, try:
      Get-MailboxDatabase DB_xxx | Get-Mailbox | ForEach { .\Remove-MessageClassItems.ps1 -Mailbox $_.Identity -Impersonation -Verbose -DeleteMode SoftDelete -MessageClass EnterpriseV
      ault -PartialMatching -ScanAllFolders -Before ((Get-Date).AddDays(-730)) }

      Like

      • Hi Michel:
        Thanks for your quick response. !!!!That worked¡¡¡¡

        I only had to change $_.Identity for $_.Alias to make it work, as the Identity value has spaces, and Alias does not.

        Thank you very much for your help. It saved me a lot of time

        Best regards

        Patxi

        Like

  38. Hi Michel:
    Another tricky question, at least for me…
    Running your script to all mailboxes of a specific database, asks for deletion confirmation for every mailbox it encounters. Is there any way to automate this response (for example A to delete all ítems, etc.) to avoid typing this for every mailbox?

    Thanks for your great Support.

    Patxi

    Like

  39. Hi Michel,
    Awesome script. It’s weird that MS decided not to allow this in the “advanced” search applets of Exchange 2013.
    I do have a feature request:
    It is possible to move the items to the deleted items folder, but what I want to do is to export the items to a PST file and remove them. If it was possible to move the items to a random new empty folder, that would be great. If this folder could be located in a different mailbox, that would be even better. Is this something that would be hard to add on?
    Thank you in advance!
    Milan

    Like

    • Exporting to PST yes, as I’m interacting with the mailbox through EWS and have no ‘PST library’ at my disposal. I could create a ‘Duplicates’ folder and dump everything in there; noted on the wish list.

      Like

      • Ofcourse, I thought exporting to a PST would be difficult at the least. But moving items to a new seach folder should be straight forward …
        Thank you for putting it on the wish list.

        Like

      • Could you give an indication on how much trouble this would be and when you could possibly come around to it, if ever? I apologise for sounding a bit snobby, i don’t mean to be, but we’re kind of in a jam with this being the only option and no EWS expertise or experience…

        Bedankt voor het antwoord alvast!

        Like

        • As a work-around, could you not clear out the deleted items folder temporarily, and allow for a “Move to Deleted Items” execution? This would isolate your results into the Deleted Items Folder.

          Like

          • This is not an option, users often have good reasons for keeping messages in their Deleted Items Folder (I only have one, but hey …). Also using the Deleted items folder for live data, or actually anything else but junk, makes my techy-sences tingle inappropriately.

            Like

  40. I’m no Powershell expert, but I can’t seem to be able output this to a log-file using redirection or piping to an “out-file.”

    Am I missing something easy?

    Like

  41. For the record, the following works to pipe the Get-Mailbox command to the script:

    Get-Mailbox -database databasename | select samaccountname | Select-Object @{ expression={$_.samaccountname};label=’Mailbox’ } | .’C:scriptsRemove-MessageClassItems.ps1′ -MessageClass IPM.Note.EnterpriseVault.Shortcut -Confirm:$false -DeleteMode SoftDelete -Verbose -Impersonation -ScanAllFolders -Before ((Get-Date).AddDays(-30))

    Like

  42. I am getting this error while deleting the EV shortcuts from Office 365 mailbox:

    Remove-MessageClassItems.ps1 : Can’t access mailbox information store
    At line:1 char:1

    I am running this command:

    .\Remove-MessageClassItems.ps1 -Mailbox user@domain.com -Verbose -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault.Shortcut -MailboxOnly -WhatIf -Server outlook.office365.com

    The account used to run this command has got the Full Access and Impersonation rights on the mailbox. Any help with this will be appreciated a lot.

    Thanks in advance.

    Like

      • I do not have mailto in the command which I am running, it’s just when you hover your mouse you get to see that, which is expected, I think. The same command worked on one of my colleague’s machine. I have installed the same version of EWS Managed API, still no go. Will try few more things and keep you updated.

        Like

        • Ah, I replied to the notification mail which showed the mailto HTML tag as plain text. Odd. Anyway, cmd looks ok – can’t access information store is usually something with access (permissions). You did specify Credentials, providing credentials with sufficient access?

          Like

  43. Michel,

    I tried to ensure due diligence in reading all the comments…
    It appears this methodology deletes the emails of said message class before export… is there a method you could describe to export a pst that simply had a specific message class excluded or export a pst that is only of a specific message class (ipm.note e.g.)?

    Like

    • In modern Exchange, you can leverage the Search-Mailbox cmdlet to find messages based on certain conditions, and export the results a different mailbox with folder specification (and from there you can export it to PST). E.g., something like this should work: Get-Mailbox | Search-Mailbox -SearchQuery ‘MessageClass:IPM.Note’ -TargetMailbox ‘TargetMailbox’ -TargetFolder “Query1”

      Like

        • My bad – you can use -MessageType but that is limited to predefined types (eg Email). Specifying -SearchQuery ‘IPM.NOTE.EnterpriseVault.Shortcut’ works though, but might match on too much items (not bound to message type).

          Like

    • You need to use ews to move items to a separate folder and then export the mailbox while either selecting or excluding the particular folder you moved the items to.

      Like

  44. I’m trying to run this against our Office 365 environment to try and clean out EnterpriseVault stubs and am getting all kinds of errors, I’m guessing because it’s running Exchange 2016?

    .\Remove-MessageClassItems.ps1 -Mailbox me@company.com -Verbose -DeleteMode MoveToDeletedItems -MessageClass IPM.Notes.EnterpriseVault.Shortcut -ArchiveO
    nly -WhatIf -Server outlook.office365.com
    VERBOSE: Loading C:\Program Files\Microsoft\Exchange\Web Services\2.2\\Microsoft.Exchange.WebServices.dll
    Processing mailbox me@company.com (me@company.com)
    VERBOSE: Set to trust all certificates
    New-Object : Multiple ambiguous overloads found for “ExchangeService” and the argument count: “1”.
    At C:\PowerShell\Remove-MessageClassItems.ps1:399 char:22
    + $EwsService= New-Object Microsoft.Exchange.WebServices.Data.ExchangeServ …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [New-Object], MethodException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

    The property ‘UseDefaultCredentials’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\PowerShell\Remove-MessageClassItems.ps1:400 char:9
    + $EwsService.UseDefaultCredentials= $true
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    The property ‘UseDefaultCredentials’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\PowerShell\Remove-MessageClassItems.ps1:413 char:13
    + $EwsService.UseDefaultCredentials= $true
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    VERBOSE: Using Exchange Web Services URL https://outlook.office365.com/EWS/Exchange.asmx
    The property ‘Url’ cannot be found on this object. Verify that the property exists and can be set.
    At C:\PowerShell\Remove-MessageClassItems.ps1:424 char:13
    + $EwsService.Url= “$EwsUrl”
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

    VERBOSE: DeleteMode is MoveToDeletedItems
    VERBOSE: PartialMatching is False
    VERBOSE: Removing messages of class IPM.Notes.EnterpriseVault.Shortcut

    Like

  45. Great script and solved a lot of manual work.

    However as a programmer i would recommend to use and handle try catch blocks whenever possible and accordingly. It’s a hassle to figure which line of code caused an error while each time an error message called “Can’t access mailbox information store” appears!

    Like

    • As a programmer, I would say the error is already thrown in a catch block, but nested ones don’t go well indeed.
      Only thing is I could add $Error to the output, however it’s usually the same one (access denied). Cheers!

      Like

  46. Hi Michael,

    I’m getting this error now when i use your script
    “Can’t access mailbox information store: cannot find an overload for “isequlto” and the argument count: “3′” at line:1 char:1

    I’m not using any folder switch but even when i do, it throws that same error. I’m using ews 2.0 api

    Like

  47. hi there, trying to run this, great sript by the way
    Import-CSV users.csv1 | Remove-MessageClassItems.ps1 -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDelete -Impersonation

    and keep getting command failure about not able to take pipeline, what am i doing wrong please

    Like

    • What’s in the CSV? Should be something like:

      Identity
      MailAlias

      Then:
      Import-CSV users.csv | .\Remove-MessageClassItems.ps1 -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode HardDelete -Impersonation

      Also note that for Office 365 (use e-mail addresses as Identity) you need to specify/use Credential; for on-prem, your current credential will be used.

      Like

      • CSV file had
        identity
        user1
        user2

        here is output

        [PS] C:\>Import-CSV c:\users.csv | Remove-MessageClassItems.ps1 -MessageClass IPM.Note.EAS -DeleteMode HardDelete -Imper
        sonation

        Security Warning
        Run only scripts that you trust. While scripts from the Internet can be useful, this script can potentially harm your
        computer. Do you want to run C:\Windows\system32\Remove-MessageClassItems.ps1?
        [D] Do not run [R] Run once [S] Suspend [?] Help (default is “D”): r
        C:\Windows\system32\Remove-MessageClassItems.ps1 : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
        At line:1 char:55
        + Import-CSV c:\users.csv | Remove-MessageClassItems.ps1 <<<< -MessageClass IPM.Note.EAS -DeleteMode HardDelete -Imper
        sonation
        + CategoryInfo : InvalidArgument: (@{Identity=AshruthB}:PSObject) [Remove-MessageClassItems.ps1], Paramet
        erBindingException
        + FullyQualifiedErrorId : InputObjectNotBound,Remove-MessageClassItems.ps1

        Like

        • What version of the script are you using? Latest version is 1.52, and this should work:
          [PS] C:\>Import-CSV c:\users.csv | .\Remove-MessageClassItems.ps1 -MessageClass IPM.Note.EAS -DeleteMode HardDelete -Impersonation

          Like

  48. Hello Michel,

    thank you for the great script! It is really useful. I would like to ask a question since my scripting skills are not up to par to make necessary adjustments.

    So my case is as follows: I need to delete EV shortcuts older than 90 days in all the folders excluding one folder AND its subfolders.
    I started with running the script with -ExcludeFolder specifying needed folder as its parameter. Script runs and excludes the folder that I specified, but still wants to process its subfolders and delete EV shortcuts from them.

    I see there is a line of code $FolderView.Traversal= [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep that may have something to do with this behavior.

    However, from what I read and understood the logic of he Folder Traversal, this part should ensure that subfolders of the -ExcludeFolder will be excluded by the script too, which is not happening.

    Can you please help to figure this out?

    Like

    • Currently the folder(s) specified only apply to that folder, not subfolders. Logic is coming to make this a lot more flexible (already implemented for the duplicate removal script). It’s more than traversal setting, which determines if search is depth first or breadth first. Stay tuned!

      Like

  49. Michael

    Great script – does almost precisely what I need. But I’ve got a nervy client who would like to move the stubs into a new folder so they can be examined prior to deletion. I thought of using the MoveToDeletedItems, but then the stubs would be lumped in with the normal cruft.

    If there any way to have an option to move all of the items found into a new folder – no need to recreate the folder structure?

    Like

  50. Michael

    Great script – does almost precisely what I need. But I’ve got a nervy client who would like to move the stubs into a new folder so they can be examined prior to deletion. I thought of using the MoveToDeletedItems, but then the stubs would be lumped in with the normal cruft.

    If there any way to have an option to move all of the items found into a new folder – no need to recreate the folder structure?

    Like

        • You could also temporarily clean out the Deleted Items folder (prior to execution), or simply sort by message class in Outlook (after using MoveToDeletedItems) and then move to a new folder at that point.

          Like

      • Just a note on the -Report option. When I used it, I received the following error when processing the Inbox

        Processing folder \Inbox
        WARNING: Error performing operation FindItems with Search options in Inbox. Error: Exception calling “FindItems” with “2” argument(s): “E
        xchange Web Services are not currently available for this request because none of the Client Access Servers in the destination site could process the re
        quest.”

        There were 112,000 items in there, so that might have been the limiting factor.

        Like

  51. First off, i love your blog and i am glad you are keeping up with it!
    Ido have a question in regard of the script you had updated for searching certain message class and deleting them; i am working on a migration project from EnterpriseVault to Exchange Online and we have ophan EV items due to restore that we would like to convert instead of deleting them. we do not care if the attachement isnt recovered…

    WOuld it be possible to adjust your script so that it convert the message class from “IPM.NOTE.EnterpriseVault.Shortcut” to “IPM.NOTE” ?

    Like

      • Hi, WHen I add -replaceClass to replace pendingArchive, I receive error for the command.

        VERBOSE: Changing messages of class IPM.Note.EnterpriseVault.PendingArchive to IPM.Note
        VERBOSE: Constructing folder matching rules
        WARNING: Cannot bind to MsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s): “Exchange
        Server doesn’t support the requested version.”
        WARNING: Cannot bind to ArchiveMsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s):
        “Exchange Server doesn’t support the requested version.”

        Thanks for your advice.

        Like

  52. Great blog, and thank you for your work on this script!!

    I am seeing the following error:

    VERBOSE: Loading D:\temp\EV script\Microsoft.Exchange.WebServices.dll
    VERBOSE: Loaded EWS Managed API v15.00.0913.015
    VERBOSE: Set to trust all certificates
    Processing mailbox username(username@ourdomain.ca)
    VERBOSE: Looking up EWS URL using Autodiscover for Jusername@ourdomain.ca
    VERBOSE: Using EWS on CAS https://email.ourdomain.ca/EWS/Exchange.asmx
    VERBOSE: DeleteMode is MoveToDeletedItems
    VERBOSE: Removing messages of class *EnterpriseVault*
    VERBOSE: Removing messages older than 01/09/2017 00:00:00
    VERBOSE: Constructing folder matching rules
    WARNING: Cannot bind to MsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s): “Exchange
    Server doesn’t support the requested version.”
    VERBOSE: Processing primary mailbox jharany
    WARNING: Cannot bind to DeletedItems – skipping. Error: Exception calling “Bind” with “2” argument(s): “Exchange Server
    doesn’t support the requested version.”
    VERBOSE: Collecting folders containing e-mail items to process
    WARNING: Error performing operation FindFolders with Search options in . Error: Exception calling “FindFolders” with
    “3” argument(s): “Value cannot be null.
    Parameter name: parentFolderId”

    …from there it repeats.
    Many thanks again. I am unsure what ‘Exchange Server doesn’t support the requested version’ may refer to.

    Like

      • OK I’ve resolved the Exchange version error.

        But the error below persists (and repeats):
        VERBOSE: Loading D:\Temp\path\Message Class scripts\Microsoft.Exchange.WebServices.dll
        VERBOSE: Loading module from path ‘D:\Temp\Jeremy\Message Class scripts\Microsoft.Exchange.WebServices.dll’.
        VERBOSE: Loaded EWS Managed API v15.00.0913.015
        VERBOSE: Set to trust all certificates
        Processing mailbox flast (first.last@ourdomain.ca)
        VERBOSE: Looking up EWS URL using Autodiscover for first.last@ourdomain.ca
        VERBOSE: Using EWS on CAS https://email.ourdomain.ca/EWS/Exchange.asmx
        VERBOSE: DeleteMode is MoveToDeletedItems
        VERBOSE: Removing messages of class IPM.Note.EnterpriseVault.Shortcut
        VERBOSE: Removing messages older than 01/09/2017 00:00:00
        VERBOSE: Constructing folder matching rules
        VERBOSE: Processing primary mailbox flast
        VERBOSE: Collecting folders containing e-mail items to process
        WARNING: Error performing operation FindFolders with Search options in . Error: Exception calling “FindFolders” with
        “3” argument(s): “Value cannot be null.
        Parameter name: parentFolderId”

        Like

        • Opposite, should be run from basic PowerShell session. Please use EWS Managed API 2.2 (you have v15.00.0913.015 installed which is 2.0 IIRC), v2.2 supports additional EWS calls which are required.

          Like

          • Thanks for the suggestion.

            I have indeed downloaded 2.2 from https://www.microsoft.com/en-us/download/details.aspx?id=42951. Problem being that in that link, MS describes it as Version: 15.00.0913.022, however, when extracted, the .dll responds like so:

            VERBOSE: Loading C:\Program Files\Microsoft\Exchange\Web Services\2.2\\Microsoft.Exchange.WebServices.dll
            VERBOSE: Loading module from path ‘C:\Program Files\Microsoft\Exchange\Web
            Services\2.2\Microsoft.Exchange.WebServices.dll’.
            VERBOSE: Loaded EWS Managed API v15.00.0913.015

            …and terminates with the error:

            D:\Temp\user\Message Class scripts\Remove-MessageClassItems.ps1 : Cannot access mailbox information store, error: Can
            not convert the “System.Object[]” value of type “System.Object[]” to type “Microsoft.Exchange.WebServices.Data.FolderId
            “.
            At line:1 char:31
            + .\Remove-MessageClassItems.ps1 <<<< -Identity f.last -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode MoveToDeletedItems -Before 1/9/2017 -mailboxonly -IncludeFolders Inbox -verbose
            + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
            + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Remove-MessageClassItems.ps1

            If there is a newer version of the .dll, that is, 15.00.0913.022, I cannot seem to locate it 😦

            Like

  53. Hi Michel,
    When i run the syntax with switch -replaceClass. I got this message.

    VERBOSE: Changing messages of class IPM.Note.EnterpriseVault.PendingArchive to IPM.Note
    VERBOSE: Constructing folder matching rules
    WARNING: Cannot bind to MsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s): “Exchange
    Server doesn’t support the requested version.”
    WARNING: Cannot bind to ArchiveMsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s):
    “Exchange Server doesn’t support the requested version.”

    if without running the -replaclass, i got it work.

    Like

  54. Hello,

    Just need to find out, if i use this command below does it delete the pendingArchive too?

    -DeleteMode Harddelete -MessageClass IPM.Note.EnterpriseVault.Shortcut

    Thank you

    Like

  55. Hi Michel,

    Does the script recurse through non-standard subfolders? At the top level of every mailbox, we have ‘Archive X Years’ folders for 3, 5, 7, 10 years and Forever. Some of these folders have several levels of subfolders depending on how the user has their mailbox set up. After running the script using credentials, it will clean up the top levels of the Archive X Years, but items remain in the subfolders and the script says ‘0 items processed in 00:00:11…” I’ve tried it with the ScanAllFolders option and by specifying just the Archive folders:

    .\Remove-MessageClassItems.ps1 -Identity first.last@contoso.com –Credentials $Credentials -Verbose -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault.Shortcut –MailboxOnly –IncludeFolders \archive*\*

    Like

  56. Thank you for the script. When I run it on my account using my credentials it works great. But when I run it against other users using an account with Full Access Permissions I receive:
    VERBOSE: Loading C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.
    Exchange.WebServices.dll
    VERBOSE: Loaded EWS Managed API v15.00.0913.015
    VERBOSE: Set to trust all certificates
    VERBOSE: Using credentials migrationwiz@company.com
    Processing mailbox user@company.mail.onmicrosoft.com

    VERBOSE: Using Exchange Web Services URL https://outlook.office365.com/EWS/Excha
    nge.asmx
    VERBOSE: DeleteMode is MoveToDeletedItems
    VERBOSE: Removing messages of class *ExShortcut*
    VERBOSE: Constructing folder matching rules
    WARNING: Cannot bind to MsgFolderRoot – skipping. Error: Exception calling “Bind
    ” with “2” argument(s): “The request failed. The remote server returned an error
    : (503) Server Unavailable.”
    WARNING: Cannot bind to ArchiveMsgFolderRoot – skipping. Error: Exception callin
    g “Bind” with “2” argument(s): “The request failed. The remote server returned a
    n error: (503) Server Unavailable.”
    VERBOSE: Processing user@company.mail.onmicrosoft.com finished

    Command I am using is:
    $Credentials= Get-Credential
    ./Remove-MessageClassItems.ps1 -Server outlook.office365.com -Identity user@company.mail.onmicrosoft.com -DeleteMode MoveToDeletedItems -MessageClass *ExShortcut* -Credentials $Credentials -Verbose

    What am I doing wrong?

    Like

  57. Hi Michel,
    Thanks a lot for the script and actively supporting it.

    Can we use the script to remove messages from Recoverable Items of both primary mailbox and online archive? We have few users with 100 GBs of data already accumulated withing Recoverable Items due to a bug in the 3rd party Outlook plugin. There is a retention policy (aka hold in place).

    Thank you.

    Like

    • In the current version no, but adding Recoverable Items to the process is fairly easy. Standby 🙂 Do note you need to remove the hold prior to cleaning up Recoverable Items, or it won’t help. Also note that removing the Hold may impact compliance due to the system removing items beyond retention.

      Like

      • Thank you Michel,
        I am aware of the pre-reqs to delete message for mailboxes on-hold. I think we will need to temporary disable the retention policy for a user, then run your magic script and hope that managed folder assistant won’t kick-in before we re-enable the retention. The problem is that there are literally 100GB of duplicates, so it will take ages to clean up (we are on Office 365 and likely will be throttled by EOL).
        BTW, I guess you are aware that Recoverable folder contains few subfolders 🙂

        Looking forward to try the script in action.

        Like

  58. Hi Michel,
    First of all, great post and script, save us a lot of time to do the manual activity!
    However, I’ve a request to modify the script to either date range or after certain date. I tried to change
    If ( $Before) {
    $ItemSearchFilterCollection.add( (New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsLessThan( [Microsoft.Exchange.WebServices.Data.ItemSchema]::DateTimeReceived, $Before)))

    to IsMoreThan but not work. Can you please share some tips which line i can start with?

    Like

  59. Hi Michel,
    really great script, i got some errors running it. my exchange is a 2010 in german language
    AUSFÜHRLICH: Collecting folders containing e-mail items to process
    WARNUNG: Error performing operation FindFolders with Search options in . Error: Ausnahme beim Aufrufen von
    “FindFolders” mit 3 Argument(en): “Der Wert darf nicht NULL sein.
    Parametername: parentFolderId”
    AUSFÜHRLICH: Adding folder \
    WARNUNG: Error performing operation FindFolders with Search options in . Error: Ausnahme beim Aufrufen von
    “FindFolders” mit 3 Argument(en): “Der Wert darf nicht NULL sein.
    Parametername: parentFolderId”
    any ideas ? thanks

    Like

  60. Hi, this is really useful and effective, but is there any way to avoid the prompts to process the deletion? I’ve tried -force (not recognised) and -confirm $false (seems to accept it and then continues to prompt for each mailbox). I am running it with impersonation which is working fine outside the prompts. Cheers, Rob

    Like

  61. Followed this tutorial and also checked the comments but I can’t seem to get this script to work. I am receiving the following messages:

    WARNING: Cannot bind to MsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”
    WARNING: Cannot bind to ArchiveMsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”

    I’ve installed EwsManagedApi.msi and can connect to O365 and run regular PowerShell successfully. I am a global admin and gave myself full mailbox permissions to the mailbox I am targeting in my test.

    Liked by 1 person

  62. Good Morning Michel and thanks for this amazing script, this will drastically improve/fasten and easy mailbox archiving and deletion. Was looking for that option since a long time
    One question (and maybe idea of improvement) :
    Is it possible to specify multiple classes in -MessageClass switch? Like *EnterpriseVault*, *Meeting*, *Task*
    Reason for that is: if you want to delete different classes you have to run the script for each class and it takes quite long when some people have thousands of folders in their mailbox
    Other possibility would be to scan all folders only once and being able to imbricate functions
    Looking forward to hear from you … and again a BIG THANKS for your job

    Like

  63. Pingback: Remove EnterPriseVault Shortcut Class Message after Office 365 Migration | Tech Wizard

  64. Hi Michael, I tried the latest version of the script but am still getting the following:

    WARNING: Cannot bind to MsgFolderRoot – skipping. Error: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”

    I assigned the ApplicationImpersonation role to the service account and given the service account full access to the mailbox.

    I am using the following syntax:

    .\Remove-MessageClassItems.ps1 -Identity firstname.lastname@company.mail.onmicrosoft.com -Credentials $UserCredential -Server outlook.office365.com -Verbose -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault* -ScanAllFolders -MailboxOnly -impersonation

    Any ideas?

    Thanks, Alan C

    Like

  65. Hi Michel

    Could you please provide more details on the Modern Auth, as I’m still failing to authenticate I’ve created an app registration with app level ews permissions, and using the convert-to-string to for the -secret parameter, however getting error cannot bind to MsgFolderRoot. when i check the app registration i’m not showing any authentication attempts.

    Do we need to add a script block to obtain the auth token, or is this built into your script. If you could please provide details it would be a great help

    Like

    • Obtaining tokens etc. is in the script; you just need to configure app with proper permissions. You did configure basics such as autodiscover, otherwise -server outlook.office365.com might help. Also, use Verbose to get a sense of what path it walks regarding connecting.

      Like

  66. Hi, and thnak you so much for the script. I reeally need it! However, I am unable to run it.
    No matter what parameter I use, it keeps saying:

    PS D:\> ./Remove-MessageClassItems.ps1 -Identity myuser -Impersonation
    D:\ADMHME\39\SCRIPT\Remove-MessageClassItems.ps1 : Parameter set cannot be resolved using the specified named parameters.
    At line:1 char:1
    + ./Remove-MessageClassItems.ps1 -Identity myuser …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (:) [Remove-MessageClassItems.ps1], ParameterBindingException
    + FullyQualifiedErrorId : AmbiguousParameterSet,Remove-MessageClassItems.ps1

    Please help. Thank you in advance. I already have the latest DLL version 2.2.1.2.

    Like

    • AmbiguousParameterSet – PowerShell cannot determine which set of parameters you are addressing and thus which ones it may me missing. If the example you pasted is complete, it’s missing the mandatory MessageClass parameter.

      Like

      • Hi,
        The mandatory parameters have been set, also MessageClass.
        What I am trying to accomplish is report as CSV or HTML the number of Enterprise Vault shortcuts in all mailboxes.
        Thank you in advance.

        Like

          • Hi Michel,
            Thank you! It was “-Credentials” parameter missing.
            Now I have the error:

            ….\Remove-MessageClassItems.ps1 : Cannot access mailbox information store for ihmerouane@domain.com: Cannot find an overload for “PropertySet” and the
            argument count: “3”.
            At line:1 char:1
            + .\Remove-MessageClassItems.ps1 -Mailbox imerouanh -MessageClass “IPM. …
            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
            + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Remove-MessageClassItems.ps1

            Like

            • With the “-Verbose” parameter (just before the error):

              VERBOSE: Module Microsoft.Exchange.WebServices v2.2.1.0 already loaded
              VERBOSE: Module Microsoft.Identity.Client v4.25.0.0 already loaded
              VERBOSE: Using credentials iadmhme@msgroupemutuel.ch
              VERBOSE: DeleteMode is SoftDelete
              VERBOSE: Removing messages of class IPM.Note.EnterpriseVault.Shortcut
              Processing mailbox ihmerouane@msgroupemutuel.ch (ihmerouane@msgroupemutuel.ch)
              VERBOSE: Using ihmerouane@msgroupemutuel.ch for impersonation
              VERBOSE: Looking up EWS URL using Autodiscover for ihmerouane@msgroupemutuel.ch
              VERBOSE: Using EWS endpoint https://outlook.msgroupemutuel.ch/EWS/Exchange.asmx
              VERBOSE: Constructing folder matching rules
              VERBOSE: Processing primary mailbox ihmerouane@msgroupemutuel.ch
              VERBOSE: Collecting folders containing e-mail items to process

              Like

                • Hi,
                  I have tested your last release, and it works perfectly with the DLL you provide with the ZIP.
                  Great job!!

                  May I continue asking for a way to export to CSV, please?

                  Like

                  • You can use -Report to report on individual items, what WhatIf:$True to run the script without removing anything.
                    Use regular output redirection to output to file; currently no fancy object output to export to CSVs or further processing.

                    Like

              • Good news!!! It finally works!
                I reverted back to another DLL version (the one in the /bin folder of the Exchange 2016 server).

                VERBOSE: Module Microsoft.Exchange.WebServices v15.0.0.0 already loaded
                VERBOSE: Module Microsoft.Identity.Client v4.25.0.0 already loaded
                VERBOSE: DeleteMode is SoftDelete

                Is it possible to have all the result exported to CSV?
                As I said, I have to scan and report for 3’000+ mailboxes.
                Thank you in advance.

                Like

  67. Hi Thank you so so much for the script. We are migrating from Gsuite to O365 and some mailboxes have duplicated items.

    I’ve managed to install the modules and stuff, but when i run the script i got an error.

    VERBOSE: Using Exchange Web Services URL https://outlook.office365.com/EWS/Exchange.asmx
    VERBOSE: Constructing folder matching rules
    WARNING: Cannot bind to MsgFolderRoot: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”
    WARNING: Cannot bind to ArchiveMsgFolderRoot: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”
    VERBOSE: Processing ss@xxx.com finished

    Do i need any special authorization on Exchange Online to use this script ?

    Thank you

    Like

  68. Using the -MessageClass *EnterpriseVault* or even explicit IPM… I’m not seeing any results being deleted. I know I have EV Shortcuts in a specific folder, but the script says there’s 0. Any ideas?

    Like

      • Already using impersonation. Not sure what else could be wrong.

        PS D:\Scripts\Remove-MessageClassItems-master> .\Remove-MessageClassItems.ps1 -Identity EMAIL -Credentials $UserCredential -Impersonation -Server email.point72.com -Verbose -DeleteMode SoftDele
        te -MessageClass IPM.Note.EnterpriseVault* -ScanAllFolders –MailboxOnly

        Like

        • Not to ask the obvious, but since you are using BasicAuth, have you configured Application Impersonation permissions for the account you are using with UserCredential, or are you using the credentials from the account owning the mailbox? Note that for on-prem, if it’s the account you logged in with, you can use the UseDefaultCredentials switch.

          Like

  69. Hey Michel.

    Great script. Works fine, for me, with an On-premises Mailbox.
    However, when I try to do the same with an Online Mailbox I get an Error:

    VERBOSE: Module Microsoft.Exchange.WebServices v2.2.1.0 already loaded
    VERBOSE: Loading module D:\_Users\Richard\PwShFunctions\Remove-MessageClassItems-master\Microsoft.Identity.Client.dll
    VERBOSE: Loading module from path ‘D:\_Users\Richard\PwShFunctions\Remove-MessageClassItems-master\Microsoft.Identity.Client.dll’.
    Write-Error: D:\_Users\Richard\PwShFunctions\Remove-MessageClassItems-master\Remove-MessageClassItems.ps1:1247
    Line |
    1247 | Import-ModuleDLL -Name ‘Microsoft.Identity.Client’ -FileName ‘Mic …
    | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    | Problem loading module Microsoft.Identity.Client: Assembly with same name is already loaded

    Any idea?!

    Richard

    Like

  70. Hi Michel, thanks for all the work you’ve put into this already! I’m running into an error in exchange 2016. The syntax I’m using looks like this:

    .\Remove-MessageClassItems.ps1 -Identity UPN -MessageClass IPM.Note.EnterpriseVault* -Impersonation -DeleteMode MoveToDeletedItems -Credentials $creds

    I’m receiving two errors when I run this-

    1. Method invocation failed because [Microsoft.Exchange.WebServices.Data.ExchangeService] does not contain a method named
    ‘new’.
    At C:\Users\username\Documents\Remove-MessageClassItems-master\Remove-MessageClassItems.ps1:1255 char:5
    + $EwsService= [Microsoft.Exchange.WebServices.Data.ExchangeService]::new( $Ex …

    2. C:\Users\username\Documents\Remove-MessageClassItems-master\Remove-MessageClassItems.ps1 : Invalid credentials
    provided: Method invocation failed because [System.Net.NetworkCredential] does not contain a method named ‘new’.

    Any suggestions would be appreciated. I downloaded the repo from GitHub and am running it from within that folder. I’m not sure why it seems to be having a hard time with that Exchange method. Thanks!

    Like

  71. Hi Michel,

    thanks for the Script.
    I’ve tried to delete the ItemClass Value IPM.Note.EnterpriseVault.Shortcut and got no errors.
    My Problem is, that the Value still exists even after several different runs of the Script.

    Now I tried to modify the Value and get the following Error:

    Process-Mailbox : Problem modifying item: Exception calling “Bind” with “2” argument(s): “At least one recipient is not valid., The process failed to get the correct properties.”
    At C:\Admin\Remove-MessageClassItems-master\Remove-MessageClassItems.ps1:1421 char:33
    + … If (! ( Process-Mailbox -Identity $emailAddress -EwsService $EwsS …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Process-Mailbox

    Did you have an Idea, what the problem is?

    I’m using the Script with OAuth.

    thank you in advance
    Felix

    Like

  72. Hello
    Thanks for the scripts .

    i would like to use it to remove EV shortcut post migration to exchange online.

    i’m occured the following issue.
    VERBOSE: Using Exchange Web Services URL https://outlook.office365.com/EWS/Exchange.asmx
    VERBOSE: Constructing folder matching rules
    WARNING: Cannot bind to MsgFolderRoot: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”
    WARNING: Cannot bind to ArchiveMsgFolderRoot: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (401) Unauthorized.”

    The command that i’m launch is:
    .\Remove-MessageClassItems.ps1 -Identity $username -MessageClass IPM.Note.EnterpriseVault.Shortcut -DeleteMode MoveToDeletedItems -Impersonation -Verbose -Credentials $credentials -Server outlook.office365.com

    I’ve read that maybe i can solve my issue with App Password, but i’m not able to create it.

    I know too that basic auth is depreciate since many month.

    could you please tell me if there are another solution to clear all EV shortcut after migrate Mailbox to Exchange online ?

    Thanks

    Like

    • For EXO, you need Modern Authentication. Credentials is Basic Authentication, which is only for local usage.
      For Modern Authentication, you need to register an enterprise app in Azure AD. You need to configure the app with proper permissions (app permission full_access_as_app) and use certificate (can be self-signed) – or if you must Secret – to authenticate. How-to is mostly in the 2nd part of this blog, just use full_access_as_app instead of Exchange.ManageAsApp: https://eightwone.com/2020/08/05/exchange-online-management-using-exov2-module/
      Then, run the script with parameters Organization (your tenant ID), AppId (ID of the thing your registered) and one of the following:
      – CertificateThumbprint of the certificate to use for authentication (after loading it in your personal certificate store)
      – CertificateFile together with CertificatePassword (pfx + password as secret string)
      – Secret

      Like

      • Thanks for your quick feedback

        I’m trying to run the following command but it return to me an error

        .\Remove-MessageClassItems.ps1 -Server outlook.office365.com -Credentials $Credentials -Identity $UserName -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault* -CertificateThumbPrint $CertThumb -ClientId $AppID -TenanId $TenantID

        Return ==> Remove-MessageClassItems.ps1 : Parameter set cannot be resolved using the specified named parameters.
        At line:1 char:1
        + .\Remove-MessageClassItems.ps1 -Server outlook.office365.com -Credent …
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : InvalidArgument: (:) [Remove-MessageClassItems.ps1], ParameterBindingException
        + FullyQualifiedErrorId : AmbiguousParameterSet,Remove-MessageClassItems.ps1

        i don’t know where is my issue

        Like

          • I’ve trying to re-run with:

            – Only Tenant ID : Same error

            .\Remove-MessageClassItems.ps1 -Server outlook.office365.com -Credentials $Credentials -Identity $UserName -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault.Shortcut -WhatIf -Verbose -TenantId $TenantID

            – Only ClientID : Same Error

            .\Remove-MessageClassItems.ps1 -Server outlook.office365.com -Credentials $Credentials -Identity $UserName -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault.Shortcut -WhatIf -Verbose -ClientId $AppID

            – Only CertificateThumbPrint : Same Error
            .\Remove-MessageClassItems.ps1 -Server outlook.office365.com -Credentials $Credentials -Identity $UserName -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault.Shortcut -CertificateThumbprint $CertThumb -Report -WhatIf -Verbose

            I’ve compare with example in script and i don’t see where i’ve made an mistake..

            Could you please giving to me an full example of what i need to add on parameter ?

            Thanks

            Like

            • # Modern Auth, certificate in your local certificate store
              .\Remove-MessageClassItems.ps1 -Identity michel@contoso.com -MessageClass IPM.Note.EnterpriseVault.Shortcut -TenantId -ClientId -CertificateThumbprint

              # Modern auth, certificate in external file
              $PfxPwd= ConvertTo-SecureString ‘Hello’ -AsPlainText -Force
              .\Remove-MessageClassItems.ps1 -Identity michel@contoso.com -MessageClass IPM.Note.EnterpriseVault.Shortcut -TenantId -ClientId -CertificateFile C:\Temp\MyCert.pfx -CertificatePassword $PfxPwd

              # Modern auth, secret
              $Secret= Read-Host ‘Secret’ -AsSecureString
              .\Remove-MessageClassItems.ps1 -Identity michel@contoso.com -MessageClass IPM.Note.EnterpriseVault.Shortcut -TenantId -ClientId -Secret $Secret

              Do *not* specify multiple authentication methods (eg Credentials *and* one of the certificate ones or tenantid or appid), as that will create ambiguity in what authentication method to use.

              Like

              • Many thanks

                I’ve test quickly and retester tomorrow

                but actually i’ve new error code aith both command :

                .\Remove-MessageClassItems.ps1 -Identity $UserName -MessageClass IPM.Note.EnterpriseVault.Shortcut -TenantId ‘xxxxxxxxxxxxxxxxxxx’ -ClientId ‘xxxxxxxxxxxxx’ -CertificateFile .\mycert.pfx -CertificatePassword $CertificatePassword -Server outlook.office365.com -Report -WhatIf -Verbose

                .\Remove-MessageClassItems.ps1 -Identity $UserName -MessageClass IPM.Note.EnterpriseVault.Shortcut -TenantId ‘xxxxxxxxxxxxxxxxxxx’ -ClientId ‘xxxxxxxxxxxxx’ -CertificateThumbprint ‘xxxxxxxxxxxxxxx’ -Server outlook.office365.com -Report -WhatIf -Verbose

                WARNING: Cannot bind to MsgFolderRoot: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (403) Forbidden.”
                WARNING: Cannot bind to ArchiveMsgFolderRoot: Exception calling “Bind” with “2” argument(s): “The request failed. The remote server returned an error: (403) Forbidden.”

                or other error code

                .\Remove-MessageClassItems.ps1 -Identity $UserName -MessageClass IPM.Note.EnterpriseVault.Shortcut -TenantId ‘xxxxxxxxxxxxxxxxxxx’ -ClientId ‘xxxxxxxxxxxxx’ -Server outlook.office365.com -Report -WhatIf -Verbose -Secret $Secret

                WARNING: Cannot bind to MsgFolderRoot: Exception calling “Bind” with “2” argument(s): “Credentials are required to make a service request.”
                WARNING: Cannot bind to ArchiveMsgFolderRoot: Exception calling “Bind” with “2” argument(s): “Credentials are required to make a service request.”

                Like

                • Sounds like permission. Make sure your app is configured correctly with correct permissions (full_acces_as_app), Exchange Web Services is allowed as protocol and not blocked for unknown agents, Conditional Access is not blocking, etc.

                  Like

                  • thanks again for your feedback

                    i’ve add globaladmin rights on my App Registration this morning
                    i don’t see any connexion signin log on azure and i can’t exclude app registration from conditional policy.

                    can i request more help to know where / how i can check and solve the permission issue ?

                    many thanks

                    Like

                    • Hi, forget what i’m sayning,

                      I’ve successfully run your script

                      Many many thanks for your helps and for the script , it will help me for my job.

                      Like

  73. Hi,

    I am having issues with script on O365. Comamdn run:

    .\Remove-DuplicateItems.ps1 -mailbox me@xxx.com -type mail -Impersonation -DeleteMode MoveToDeletedItems -Retain oldest -Report >> report1.txt -server outlook.office365.com -TenantId xxx -ClientId xxx -secret $secret -Mode full -Verbose

    It connects to mailbox successfully, but any folder that contains data throughs the following error:

    Processing folder \Inbox
    WARNING: Error performing operation FindItems without Search options in Inbox. Error Exception calling “FindItems” with “1” argument(s): “Object reference not set to an instance of an object.”

    Folders with no data seem to complete:

    Processing folder \Junk Email
    VERBOSE: Cleaning unique list (Finished Folder)
    Processing folder \Sent Items
    VERBOSE: Cleaning unique list (Finished Folder)

    Do you have any ideas?

    Thanks

    Like

  74. Hi Michael, thank you for sharing the script, have been using the dedupe one and it saved alot of headaches!

    Do you know if this script can be modified to search for a custom email attribute specifically “Content-Class” and search for a value present there even if null?

    Centres around migration issues with EV and another 3rd party MAPI tool which is looking at these values to process mail.

    Thanks
    Rich

    Like

  75. Any suggestions on how to get the script to work when I am getting this:

    ===
    Problem initializing Exchange Web Services using schema Exchange2013_SP1 and TimeZone AUS Eastern Standard Time
    At E:\Scripts\Remove-MessageClassItems\Remove-MessageClassItems.ps1:1320 char:9
    + Throw( ‘Problem initializing Exchange Web Services using schema {0} and …
    ===

    Cheers!

    Like

        • Hello

          I am also getting this issue, funny thing is, it was working last week when I came to test it. I am running the the script on an Exchange On Prem 2019 server, the mailboxes live in EXO. This is the command line I am attempting

          .\Remove-MessageClassItems.ps1 -Identity my-name@company.mail.onmicrosoft.com -DeleteMode MoveToDeletedItems -MessageClass *EnterpriseVault* -MailboxOnly -Verbose -Server outlook.office365.com -ClientId clientid -TenantId tenentid -Secret $secret

          VERBOSE: Module Microsoft.Exchange.WebServices v2.2.1.0 already loaded
          VERBOSE: Module Microsoft.Identity.Client v4.25.0.0 already loaded
          Problem initializing Exchange Web Services using schema Exchange2016 and TimeZone GMT Standard Time
          At C:\TEMP\Remove-MessageClassItems\Remove-MessageClassItems.ps1:1320 char:9
          + Throw( ‘Problem initializing Exchange Web Services using sche …
          + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo : OperationStopped: (Problem initial…T Standard Time:String) [], RuntimeException
          + FullyQualifiedErrorId : Problem initializing Exchange Web Services using schema Exchange2016 and TimeZone GMT Standard Time

          The following files live in the folder I am running the script from

          Microsoft.Exchange.WebServices.Auth.dll
          Microsoft.Exchange.WebServices.Auth.xml
          Microsoft.Exchange.WebServices.dll
          Microsoft.Exchange.WebServices.xml
          Microsoft.Identity.Client.dll
          Microsoft.Identity.Client.xml

          Any help would be greatly appreciated.

          Like

  76. Superb thank you so much for your hard work. This works really well for our recently moved users to O365 using REST and automation – with a Service Principal created in O365 (App Registration) – and granted the correct rights, using a self signed Certificate. This has just made our migration project so much easier. Thank you

    Like

  77. Hi Michael, thank you for sharing the script. We are using On Prem Exchange 2019 server, and we get an error with the schema.

    + FullyQualifiedErrorId : Problem initializing Exchange Web Services using schema Exchange2016
    Do you have any plans to update the script for Exchange 2019 schema ?
    Thanks !

    Like

  78. HI

    I am getting the below error, what can be wrong?

    [PS] C:\Remove-MessageClassItems-master>.\Remove-MessageClassItems.ps1 -Identity ahmad.khattab@dar.com -Verbose -DeleteMode MoveToDeletedItems -MessageClass IPM.Note.EnterpriseVault.Shortcut -Report -WhatIf -Credentials $Credentials -Debug
    VERBOSE: Module Microsoft.Exchange.WebServices v2.2.1.0 already loaded
    VERBOSE: Module Microsoft.Identity.Client v4.25.0.0 already loaded
    VERBOSE: Using credentials darbeirut\amkhattab
    VERBOSE: DeleteMode is MoveToDeletedItems
    VERBOSE: Removing messages of class IPM.Note.EnterpriseVault.Shortcut
    Processing mailbox ahmad.khattab@dar.com (ahmad.khattab@dar.com)
    VERBOSE: Looking up EWS URL using Autodiscover for ahmad.khattab@dar.com
    VERBOSE: Using EWS endpoint https://owa.dar.com/EWS/Exchange.asmx
    VERBOSE: Constructing folder matching rules
    VERBOSE: Detected Exchange Server version 15.2.1118.21 (V2017_07_11, requested schema Exchange2013_SP1)
    VERBOSE: Processing primary mailbox ahmad.khattab@dar.com
    VERBOSE: Collecting folders to process
    C:\Remove-MessageClassItems-master\Remove-MessageClassItems.ps1 : Cannot access mailbox information store for ahmad.khattab@dar.com: Exception setting “PropertySet”: “Cannot convert the
    “Microsoft.Exchange.WebServices.Data.PropertySet” value of type “Microsoft.Exchange.WebServices.Data.PropertySet” to type “Microsoft.Exchange.WebServices.Data.PropertySet”.”
    At line:1 char:1
    + .\Remove-MessageClassItems.ps1 -Identity ahmad.khattab@dar.com -Verb …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
    + FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException,Remove-MessageClassItems.ps1

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.