This week I discovered that my Inbox had ballooned to over 20k messages. It was in desperate need of a clean-up. Here is how I automated that clean-up, getting it down to just under 1500 messages.

How did this happen? I normally use the Windows 10 Mail app, configured to download just the most recent month of messages. This keeps the number of visible messages down, and I can always open the full version of Office Outlook if I need something more. I hadn’t needed to open Office Outlook in a very long time, and had lost sight of the size of my Inbox. When my usual Mail app started crashing within 30s of startup, I suspected the size of my Inbox was the cause (it wasn’t, but that’s a separate story).

I use a simple approach to archiving messages. At the top level I have an Archive folder. Within that is a folder for each year, say 2020. Within those, there are folders for each month of the year, as 2020-01, 2020-02, and so on. I don’t use month names as I want the folders to be shown chronologically, and not every mail tool respects other ordering.

At first, I was manually selecting groups of messages and dragging them into the proper archive folders, but it soon became clear that this was tedious, error prone, and taking way too long. Worse, Outlook would become non-responsive for extended periods as it moved the messages, making it impossible to do anything else mail related.

Time to work out what I can automate through Outlook. It turned out to be surprisingly easy.

Here is the script I wrote, with commentary about what is going on, step by step. Note that I’ve wrapped some code for readability.

We begin by importing a library of generic helper methods. In this case, the only method we are using is Write-ColorText(), used to provide good logging so that we can easily see what happens.

. ./script-library.ps1

PowerShell can output Unicode characters to the console, but I needed to tickle the configuration just slightly before it worked. I also needed to save the file as UTF-8 with a BOM (byte-order-mark) at the start, else PowerShell would read the file as ASCII and will trip over any Unicode characters included.

[console]::OutputEncoding = New-Object System.Text.UTF8Encoding

We connect to the Outlook application using COM, showing a log line to the console after successfully connecting.

$outlook = new-object -com outlook.application;

Write-ColorText -Green "[✓] Connected to Outlook"

Once we have access to the application, we find the Inbox:

$mailboxName = "<mailboxname>"

$ns = $outlook.GetNameSpace("MAPI"); 
$mailbox = $ns.Folders.Item($mailboxName)
$inbox = $mailbox.Folders.Item("Inbox")

Write-ColorText -Green "[✓] Found Inbox"

And then we find the top-level archive folder.

$archiveFolder = $mailbox.Folders.Item("Archive")

Write-ColorText -Green "[✓] Found Archive Folder"

We are going to iterate over all the messages in the Inbox, checking each message to see if it needs archiving. While Outlook does not strongly define the order of message iteration, I have found that Outlook commonly groups together messages received at the same time. So, as a small optimization, I’m keeping track of the current archive folders for the current year and month, to make it faster to move messages when needed.

$yearFolder = $null
$monthFolder = $null

$messages = $inbox.Items
$messageCount = $messages.Count

Write-ColorText -Gray "[○] Scanning $messageCount messages"

To avoid completely emptying my Inbox, I define a 90-day threshold. Only messages older than this will be archived by the script.

$threshold = (get-date).AddDays(-90).Date

    -White "[○] Moving messages received prior to "

Now we are ready to iterate over all the messages.

foreach ($m in $messages) {

For logging purposes, we extract the subject of each message, truncating it to a useful length.

    if ($m.Subject.Length -gt 40) {
        $subject = $m.Subject.Substring(0, 40)+"..."
    } else {
        $subject = $m.Subject

If the message is too new, we skip it and move to the next one. You may want to comment out the logging line if you find it too chatty.

    $date = $m.ReceivedTime

    if ($date -ge $threshold) {
            -Gray "[•] Skipping " 
            -White $subject 
            -Gray " received " $date.ToString("s")

We are going to archive this message, so we check whether the year and month folders we already have are the right ones to use. If we need different folders, update our references and log that we’ve done so. This allows us to read the log and see where the script moved each message.

    $year = $date.ToString("yyyy")
    if ($yearFolder.Name -ne $year) {
        $yearFolder = $archiveFolder.Folders.Item($year)
            -Gray "[•] Found Archive for " 
            -White $year

    $month = $date.ToString("yyyy-MM")
    if ($monthFolder.Name -ne $month) {
        $monthFolder = $yearFolder.Folders.Item($month)
            -Gray "[•] Found Archive for " 
            -White $month

Finally, we do the actual move, with a log line that tells us what happened. If you’re trying this out for yourself, I’d suggest commenting out the actual move and changing the log to read “Would Move” while you check that the script is doing the right thing.

    $updated = $m.Move($monthFolder)

        -Green "[✓]" 
        -White " Moved " 
        -Green $subject 
        -White " received " 
        -Green $date.ToString("s") 
        -White " from " 
        -Green $m.SenderName 
        -White " to " 
        -Green $month

The ability to automate Office Outlook with PowerShell is quite something. With just a short piece of PowerShell, I was able to automate a tedious task in reusable fashion. Each month – or just whenever I remember – I can rerun the script and keep my Inbox tidy.

I even wrote a companion script, imaginatively called “clean-archive” that iterates through all the archive folders doing further cleaning: moving misfiled messages, deleted messages from mailing lists, and so on. You might want to do the same.


blog comments powered by Disqus

See the code

Next Post
Sharpen The Saw - June 2020  06 Jun 2020
Prior Post
Don't Gloss Over Complexity  16 May 2020
Related Posts
Browsers and WSL  31 Mar 2024
Factory methods and functions  05 Mar 2023
Using Constructors  27 Feb 2023
An Inconvenient API  18 Feb 2023
Method Archetypes  11 Sep 2022
A bash puzzle, solved  02 Jul 2022
A bash puzzle  25 Jun 2022
Improve your troubleshooting by aggregating errors  11 Jun 2022
Improve your troubleshooting by wrapping errors  28 May 2022
Keep your promises  14 May 2022
May 2020