Exchange 2013/16 Journal Mailbox Archive-to-PST PowerShell Script Automation with Reporting

Recent Exchange versions have built-in support of journaling for recording all inbound and outbound email messages for backup or compliance reasons. Overtime, the journal mailbox grows so large and needs to be trimmed or pruned. This script was written to automate the process.

This article documents a PowerShell maintenance script I have written for reporting and automating a monthly archive-to-PST process of the Exchange 2013 journaling mailbox. (Based on reader feedback, it also suits Exchange 2016 Standard.)

Archiving Concept

This script uses the PowerShell cmdlet New-MailboxExportRequest -Mailbox to export Exchange Journaling Mailbox of previous month (e.g. 2016-01-01 to 2016-01-31) as a standard PST file (e.g. archive 2016_01_31.pst) to specified locations (up to two locations) and then uses Search-Mailbox -DeleteContent to delete email messages within the date range if successful. It is designed to be run at the beginning of each month (e.g. 2/Feb/16) using Windows Task Scheduler.

Email Alerting, Reporting and Logging

A log file is created under a specified directory during each execution. An email is sent to specified email addresses with the log file when done. The email message subject indicates COMPLETE/FAILURE.

In addition, a list of email messages returned from search and deleted by Search-Mailbox cmdlet will be available as zip(csv) attachments in another specified mailbox and folder as mentioned in the message body of above email.

Safety Measure to Prevent Accidental Email Deletion

In case mail archiving has failed, script will send an alert mail and exit so that no mail deletion will occur. In technical terms, when status of Get-MailboxExportRequest is not "Completed", script keeps on waiting (looping) until it is complete. If the loop is broken or script receives any status other than "Completed", execution will be terminated and failure will be reported by email.

Assumptions and Requirements

  • PowerShell 3.0 or above
  • Execute this script on Exchange server where (preferably):
    • System Locale is English (United States)
    • Short date format (for en-US) is MM/dd/yyyy
  • Sufficient disk space for storing PST files in the specified location(s)
Note: This script was designed and tested for email messages of previous month, and has not been tested in other configurations but could still work with some tweaking. (In fact, it does not only work for journal mailbox.)

Getting Started – Script Usage and Instructions

  1. Edit variable parameters as required (See sections 1-4 in the script below.)
  2. Save the script in a local folder e.g. C:\scripts\ws_email_archiving_script.ps1
  3. To export email of last month, the script should be scheduled to run any day within the current month.

    For example, to export Jan 2016 email, running the script any day within Feb 2016 would do; the beginning of month is recommended (e.g. 2/Feb/16)

Setting up Windows Task Scheduler

  • C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
Add arguments:
  • -PSConsoleFile "C:\Program Files\Microsoft\Exchange Server\V15\Bin\exshell.psc1" -command ". 'C:\Program Files\Microsoft\Exchange Server\V15\Bin\Exchange.ps1'; &'C:\scripts\ws_email_archiving_script.ps1'"
Start in:
  • C:\Scripts
Alternatively, start the .ps1 script using a .bat script:
  • "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PSConsoleFile "C:\Program Files\Microsoft\Exchange Server\V15\Bin\exshell.psc1" -command ". 'C:\Program Files\Microsoft\Exchange Server\V15\Bin\Exchange.ps1'; &'C:\scripts\ws_email_archiving_script.ps1'"

PowerShell Script


⭐ The script can be downloaded at GitHub 

Word of Mouth

Below are some of the kind comments left by users of this script. (Thanks!)
  • "We have some very large journaling mailboxes that we've been racking our brains trying to figure out what to do with to stop the sprawl. We're on Exchange 2016 Standard, so we're limited on our database count. This script has made it really easy for us to dig out emails by month and save them off to PST that we can store out on second tier storage for archive. Thanks for this!"
  • "I've used your script for a couple of years. It's GREAT and do a GREAT job. Thank you very much!"

Release History

Ver Date Update
1.3 20200614 - Add missing bcc parameters in the first two lines of Send-MailMessage
- Change default setting of archiveEnable2ndCopy to false as it should not be commonly enabled
- Add a tip in the comment section of the script at part 3 to make it easier to configure the parameters
1.2 20171022 - Turn journal mailbox name into a variable
- Further ensure New/Get-MailboxExportRequest job name uniqueness by naming it with current date time (in yyyyMMdd_hhmmsstt)
- Remove unused variables, scriptDir, startDateShort and endDateShort
- Improve script comments
1.1 20160712 First public release
1.0 20160314 First internal release
Welcome to support this project by buying a cup of coffee if this tool is useful to you. 😊 Thanks! If you would love the script to be further developed, you may engage professional services on one of the freelancing platforms.


  1. How can I move Exchange items to a PST using PowerShell?

  2. MailboxExportRequest ContentFilter is “Received -ne $null” when querying by date

  3. Cannot get Exchange PowerShell script to run under Scheduled Tasks


  1. Great Idea! thanks for that, How i can change this for take daily backups?

    1. Hi Enes S, thanks for your interest in the script. To clarify your question, do you mean that you want to apply the script for email messages of the previous day? (Note: this script was designed and extensively tested for email messages of the previous month only)

      To answer your question, you need to (at least):
      1. Configure $endDate and $startDate (required for New-MailboxExportRequest cmdlet to export to PST)
      2. Configure $searchStartDate and $searchEndDate (required for Search-Mailbox cmdlet to delete exported email messages)
      3. Update the content of the email message to be sent after the process.

      I have updated (reorganized) the script and the blog post a bit in the hopes that it will be more readable and 'tweakable' in order to use for other purposes.

      I have tried not to miss any key things. Please bare with me if I do. :)

  2. We have some very large journaling mailboxes that we've been racking our brains trying to figure out what to do with to stop the sprawl. We're on Exchange 2016 Standard, so we're limited on our database count. This script has made it really easy for us to dig out emails by month and save them off to PST that we can store out on second tier storage for archive. Thanks for this!

    1. Thanks for sharing your experience with the script on Exchange 2016 Standard!

      Since there are newer Exchange Server major releases now, I will consider better naming the title of this post so that people using those versions may still benefit from it. (If you or anyone have done any readaptation to fit specific use cases, that would be nice to know as well.)

  3. Great Idea! thanks for that, How i can change this for take daily backups

    1. Hi Basem,

      Thanks for your question. Below are the variables you need to adjust (primarily):

      1. New-MailboxExportRequest cmdlet (handles exporting email to PST files)
      - $endDate
      - $startDate

      2. Search-Mailbox cmdlet (handles deletion of exported email)
      - $searchStartDate
      - $searchEndDate

      In case you need to further develop this script, consider hiring an expert through freelancing marketplaces. By coincidence, I just wrote an article on the topic.

  4. Hello wandersick,

    I use your script from couple of years. Its GREAT and do GREAT job. Thank you very much!!!
    I`m not good in scripting but because I have some issues with it I make modifications.
    It runs every 10 days as it keep at least 10 days history in Journal mailbox and in my case it make 10GB .pst achives.
    And because New-MailboxExportRequest -ContentFilter cant work with dates with localization other from "en-US" I added it temporally for session /Exchange 2013, Server 2012R2/
    And after exporting the .pst file is stay locked sometimes for a few more minutes, so I added a check before start coping to secondary location.

    I don't know if it's convenient to share the changes here, if you find them useful. Excuse me if it's not appropriate.
    Thanks again!
    I cant post ... too big... share in google drive:

    1. Hello connann2,

      I am glad to see you successfully implement the script in a Windows Server with a locale other than en-US and contribute your solution for that (along with improvements like checking whether exported archive file is locked and enabling ExcludeDumpster)

      Thanks for sharing your experience! In the future, I may integrate your changes into the main script so that others can be benefited. (I will credit you for that of course. Kindly let me know in case that is not OK, and feel free to let me know how you want to be credited, e.g. an URL and a name if not 'connann2'.)

      P.S. I would suggest using a service like GitHub Gist for posting code, or even better, fork the GitHub repository and then share yours. :)

      Have a good day!

    2. Hello, wandersick,

      Thank you for the reply. Don't worry about credit my name, I'll be happy if you find something useful from my modifications. I still use your work for free :)

      I forgot to mention that I saw a problem with the Search-Mailbox and New-MailboxExportRequest functions - one uses GMT time and the other local time. And so there is a difference of several hours between the limit for archived and deleted emails. So I replaced endDate = (Get-Date "00:00:00") with
      $ endDate = (Get-Date [TimeZoneInfo] :: Local) .BaseUtcOffset.ToString () ...
      This may be a local problem, I'm GMT + 2 hours.

      Thank you again!

    3. That's good to know.

      Thanks for the improvement you have brought to the script again!

    4. Hello, wandersick.
      Big thank's for your script!
      Default script is worked. But if I changed to run this script with another dates script end with error "Second file copy verification failed."
      I didn't change default $archiveEnable2ndCopy = $false
      Do you help me?

    5. Could you please try configuring a secondary copy by setting $archiveEnable2ndCopy = $true and configure a UNC path to store the secondary copy by setting $archiveFileDir2ndCopy (refer to the example in the script)? Would it work after doing so?


Post a Comment