Merge mailbox folders using EWS (with throttling support)

A PowerShell script that uses EWS (requires the EWS Managed API) to merge folders within a mailbox.  Can be used for multiple folders, and automated against multiple mailboxes.  The script handles throttling and so works against large Office 365 mailboxes.

 
 
 
 
 
5 Star
(6)
4,560 times
Add to favorites
Exchange
7/11/2019
E-mail Twitter del.icio.us Digg Facebook
Sign in to ask a question


  • Specify target folder
    3 Posts | Last post Fri 4:38 PM
    • Is it possible to specify a target folder in the target mailbox where the source items should be copied to/moved by the script or can it be requested as a new feature in the code?
      
      Say that you´ve SourceMailbox\FolderStructure and instead of placing it in TargetMailbox\FolderStructure as a 1:1 structure, then the target destination could be TargetMailbox\MovedData\FolderStructure where "MovedData" would be specified through a parameter variable and "FolderStructure" representing SourceMailbox hierarchy folder/subfolder structure  
      
    • You can do this simply by specifying the target folder in MergeFolderList.  The target folder is the root where the source folder will be copied to, and it can be anywhere in the mailbox.
      
      e.g.
      $mergeFolderList = @{"\Inbox\Moved" = "\Inbox" }
      
      will copy everything from the Inbox of the source mailbox to the folder \Inbox\Moved of the target mailbox.  Hierarchy, etc. will be preserved so long as the parameters are set (in this case, you'd also need to specify -ProcessSubfolders).  You can also move folders around within the same mailbox this way (though this example wouldn't work in that case as you can't copy a folder to a subfolder of itself).
    • David Barret,
      
      Thank you for your answer; I tried the function MergeFolderList and it worked like a charm!!!
      This script is the best thing since sliced bread... :)
  • Script hangs at # items found; attempting to copy
    2 Posts | Last post August 07, 2019
    • I am using this script but cannot get it to work. It starts and runs but the scripts seems to stall at # items found; attempting to copy. I will run for a long time but nothing seems to happen. 
      
      We have a lot of mailboxes running in Office365 we would like to merge. 
      
      Beneath the command we run
      
      .\Merge-MailboxFolder.ps1 -SourceMailbox "test@domain.com" -targetMailbox "test2@domain.com" -Copy -ProcessSubfolders -CreateTargetFolder -Office365 -Credential $credential -impersonate -logfile C:\Scripts\log.txt -tracefile C:\Scripts\trace.txt
      
      In the log file the same line keeps appearing:
      17/07/2019 15:33:58   Sending batch request to copy 3 items (3 remaining)
      
      Any suggestions?
    • I've improved the retry/error testing logic in the upcoming release, but I suspect that the main issue here is that to copy from one mailbox to another, the source mailbox needs FullAccess rights to the target mailbox.
      
      You are accessing the source mailbox using Impersonation, which means you are acting as that user.  For that user to be able to copy a message to the target mailbox, they need permission to access that mailbox - ApplicationImpersonation on the service account is not enough here, as the copy is initiated from the source mailbox in the context of the source user.  Hopefully that makes sense.
  • Creating Target Folder if missing
    2 Posts | Last post July 16, 2019
    • David Barrett,
      If the CreateTargetFolder Parameter is used, will the source folders properties be copied to the new folder such as there retention policies. I have an archive that I am consolidating it back into there primary mailbox and need to ensure there retention policies are moved. If not, that would be a great thing to add a param or just bake it in to the create folder process.
    • No properties are transferred from one folder to another, the new folder is simply a basic folder with the same name.  I could potentially add the ability to copy properties over (they would need to be defined in a list), so I will put that on the list of things to look into.  I doubt I'll have an update very soon for this.
  • Online Archives
    1 Posts | Last post June 25, 2019
    • David Barrett,
      
      Would this script work for Online Archives to move back to On-Prem Mailbox? 
      
      Thanks,
  • Powershell core support...
    2 Posts | Last post June 21, 2019
    • Can you please check and let me know if you can support powershell core from github. I tried the script and it filed with exceptions...
      
      
    • The script relies on the EWS Managed API, which will not work in .Net Core.  It would be very complex to implement this script for PowerShell Core, and isn't something likely to happen (at least, not anytime soon).
  • Can this delete a folder in the source mailbox with all its items and subfolders
    2 Posts | Last post May 30, 2019
    • Can this delete a folder in the source mailbox with all its items and subfolders?
      If so what is the command example if someone could help.
      I have a folder with 100000 items and search mailbox is limited to 10000 and it doesnt seem to be deleting them with -deletecontent -force
    • This script is intended to move/copy rather than delete.  I have written another script that should do what you want: https://gallery.technet.microsoft.com/PowerShellEWS-Update-items-48c3dcfc
      
      If Search-Mailbox can't delete the items, though, that warrants further investigation.  Is the mailbox under litigation hold or similar?
  • Access Issue
    2 Posts | Last post May 30, 2019
    • I set our Global Admin account up with Impersonation rights and tried copying an email from one Archive Mailbox to another. It binds fine, but gets Access Denied when attempting to copy. Any idea why the access is failing?
      
      Merge-MailboxFolder.ps1 version 1.1.5 starting
      
      VERBOSE: [ThrottledFolderBind]Successfully bound to folder ArchiveMsgFolderRoot (n9951528@windstream.onmicrosoft.com)
      VERBOSE: [ThrottledFolderBind]Successfully bound to folder ArchiveMsgFolderRoot (Joshua.Koffski@windstream.com)
      VERBOSE: Target folder is _Instructions
      VERBOSE: Source folder list is _Instructions
      VERBOSE: [GetFolder]Locating folder: _Instructions
      VERBOSE: [ThrottledFolderBind]Successfully bound to folder AAMkAGVlNDc0NzEzLTY3NmQtNDllZC05NDliLTM1MTUxOGFmZjVmMQAuAAAAAACDGSuLSe7cR61TS9dhAlq8AQCsUbRUTKOlS7iSNv9HUHM4AAG7AZANAAA=
      VERBOSE: Target folder located: _Instructions
      VERBOSE: Secondary folder is _Instructions
      VERBOSE: [GetFolder]Locating folder: _Instructions
      VERBOSE: [ThrottledFolderBind]Successfully bound to folder AAMkADc0MDE0NDc2LTQwODItNGI0Ni1hY2FjLWMwMTBkZWFiNWZlYwAuAAAAAACpSs1gR3ueTr1730ScJRGoAQDW1fG9vmsAS4UVBnzpEceYAAD91ebvAAA=
      
      [MoveItems]Copying from n9951528@windstream.onmicrosoft.com:\Top of Information Store\_Instructions to Joshua.Koffski@windstream.com:\Top of Information Store\_Instructions
      VERBOSE: [ThrottledFolderBind]Successfully bound to folder AAMkADc0MDE0NDc2LTQwODItNGI0Ni1hY2FjLWMwMTBkZWFiNWZlYwAuAAAAAACpSs1gR3ueTr1730ScJRGoAQDW1fG9vmsAS4UVBnzpEceYAAD91ebvAAA=
      VERBOSE: [MoveItems]Building list of items to copy
      1 items found; attempting to copy
      VERBOSE: Sending batch request to copy 1 items (1 remaining)
      VERBOSE: Waiting for 0ms
      VERBOSE: Error ErrorAccessDenied reported for item: AAMkADc0MDE0NDc2LTQwODItNGI0Ni1hY2FjLWMwMTBkZWFiNWZlYwBGAAAAAACpSs1gR3ueTr1730ScJRGoBwDW1fG9vmsAS4UVBnzpEceYAAD91ebvAADW1fG9vmsAS4UVBnzpEceYA
    • Use SOAPe or EWSEditor to identify the particular item and try to copy that way.  Does that work (I'd expect it not to)?  I'm not sure what would cause an AccessDenied error once you've got into the mailbox (which the log shows you have), so I'd be looking for any oddities about the message (is it marked private?  Encrypted?).
  • xxx items reported error during batch request (if throttled, some failures are expected)
    2 Posts | Last post May 17, 2019
    • Hi David,
      
      Thanks for the amazing script. There are some many of our processes which I think this could really help with!
      
      My first test is restoring a users Archive Mailbox to their Primary Mailbox though. All went well except I saw the following errors in the log:
      39 items reported error during batch request (if throttled, some failures are expected)
      92 items reported error during batch request (if throttled, some failures are expected)
      32 items reported error during batch request (if throttled, some failures are expected)
      Throttling detected, server requested back off for 277915 milliseconds
      Throttling budget should now be reset, resuming operations
      65 items reported error during batch request (if throttled, some failures are expected)
      
      Although my account had Impersonation rights I had neglected to add the -Impersonate switch:
      .\Merge-MailboxFolder.ps1 -SourceMailbox xxx -SourceArchive -Copy -ProcessSubfolders  -CreateTargetFolder -EwsUrl "https://outlook.office365.com/EWS/Exchange.asmx" -Credentials $creds 
      
      Is there any way to retry these failed items? I'm guessing that running the cmd again will result in duplicates since I used the -Copy option. Is the better approach to not use -Copy so you can repeatedly run the cmd until no errors are returned if necessary?
      
      Cheers, Ryan.
    • The impersonation switch shouldn't affect the moving/copying, it is only relevant to gaining access to the mailbox.  If you have delegate rights on the mailbox, then those will work just the same.
      When copying, rerunning the script will duplicate, as you note.  However, any items that are reported as an error when the script attempts to move or copy them would warrant investigation to see what they were.  The script will only skip items if it receives a permanent error from the server.  For transient errors, the operation is retried until it succeeds.  You should be able to obtain the IDs of the failed items from the log (though if you didn't create one, that will be an issue).  Once you have the IDs, you could use SOAPe to check out the items themselves.
  • Special Characaters in Folder Names
    4 Posts | Last post April 19, 2019
    • This is giving me a little bit of a headache.  We are trying to merge a folder from all of our mailboxes into a single mailbox (that logic works great).  However the script fails on with the $merge hashtable that looks like the below.
      
      $merge = @{"Inbox\This & That" = "Inbox\This & That";}
      
      
      No combination of escape characters and quotes will get me by that which is causing the script to fail.  I think the ampersand is the offending character but I just can't see to get around it in the array.  Any pointers would be appreciated.
      
    • I guess the error code would be useful :-)
      
      At C:\Users\welchdx\Downloads\Merge-MailboxFolder.ps1:103 char:27
      +     [string]$OAuthClientId = "8799ab60-ace5-4bda-b31f-621c9f6668db",
      +                              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.
      At C:\Users\welchdx\Downloads\Merge-MailboxFolder.ps1:106 char:30
      +     [string]$OAuthRedirectUri = "http://localhost/code",
      +                                 ~~~~~~~~~~~~~~~~~~~~~~~
      The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.
      At C:\Users\welchdx\Downloads\Merge-MailboxFolder.ps1:118 char:31
      +     [string]$EWSManagedApiPath = "",
      +                                  ~~
      The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.
      At C:\Users\welchdx\Downloads\Merge-MailboxFolder.ps1:127 char:21
      +     [string]$LogFile = "",
      +                        ~~
      The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.
      At C:\Users\welchdx\Downloads\Merge-MailboxFolder.ps1:133 char:26
      +     [int]$ThrottlingDelay = 0,
      +                             ~
      The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.
          + CategoryInfo          : ParserError: (:) [], ParseException
          + FullyQualifiedErrorId : InvalidLeftHandSide
    • Continuing further with this using the switch -MergeFolderList with anything in it causes the same failure.  Script runs fine without this argument but we don't want the entire contents copied.
    • Declaring the variable in the PowerShell command line worked.  Could not declare it in the code for some reason.  Great script.
  • Setting the date value for 'OnlyItemsSentRecievedBefore'
    2 Posts | Last post April 12, 2019
    • I am trying to run this script and get it to move any messages from an Inbox and Sent items before a specific date.  Every time I run it, it simply moves everything.
      
      Here is the command I am running:
      
      .\Merge-MailboxFolder.ps1 -MergeFolderList $Merge -OnlyItemsSentReceivedBefore 3/15/19 -SourceMailbox "user1@company1.com" -EwsURL "https://outlook.office365.com/EWS/Exchange.asmx" -Credentials (Get-Credential)
      
      I tried passing the date as a variable as well as different formats:
      
      3/15/19
      03/15/2019
      
      Any ideas?
      
      Thanks in Advance, this is a great script!
    • If you add -Verbose logging, the script will dump the comparisons it makes for the date (i.e. to determine if the item should be processed or not).  Does that shed any light on what is happening (might be easiest to use -LogFile parameter also)?
1 - 10 of 21 Items