Taming 14 Years of Email
Like most people who've been in IT for a while, I have an inbox that tells the story of my entire life, not just my career. This particular mess was my personal email address — an Outlook.com account I've had for fourteen years, holding somewhere north of 40,000 emails, completely unorganised, going all the way back to a version of me who apparently thought "just leave it in the Inbox" was a filing system.
It wasn't. It was a mess. And I finally decided to do something about it.
The Problem
I didn't want to delete any of it — receipts, old subscriptions, correspondence with people I've lost touch with, and the odd thing I still want to dig up years later, like the paperwork from a car finance deal I took out a while back. What I wanted was structure: emails sorted into folders by year, so I could actually find things, archive old years off, and stop scrolling through a decade and a half of history every time I searched for something.
Simple enough in theory. Forty thousand emails, manually dragged into year folders, is not a weekend project.
First Instinct: Connectors
My first thought was to reach for one of the ready-made integrations — connect Outlook up to an automation platform, let a pre-built workflow handle the sorting, job done. That's usually the "proper" way to do this kind of thing, and it's what I'd normally recommend to a client.
But after talking it through, it became clear that for a personal, one-off cleanup job, that route was overkill. Setting up and configuring a connector for something I'd likely run a handful of times didn't feel proportionate to the problem. I didn't need a maintained integration — I needed a tool, used once, that did exactly one thing well.
Plan B: Quick and Dirty PowerShell
So I pivoted to something much more "just get it done": a PowerShell script talking directly to Outlook's COM interface. Not elegant, not something I'd put in front of a client as a finished product, but exactly the kind of pragmatic tool an IT contractor reaches for when the job is "sort my own inbox out" rather than "build reusable software."
Rather than writing it from scratch myself, I used Claude to generate the script — describing what I needed, and letting it iterate with me as I hit real-world snags. The starting ask was straightforward:
"Can you create me a PowerShell script which can list my emails by a selected Year"
From there it grew, piece by piece, as I actually started using it and ran into the reality of a live mailbox rather than a tidy theoretical one. Worth saying plainly: at every stage, I read through what was generated before running it against my actual inbox. It's my personal email we're talking about, and this script has the ability to move thousands of messages around — that's not something I'd run blind, AI-written or not. Understanding what a script does before you execute it isn't optional just because something else wrote it for you.
Listing emails by year
The first version simply connected to Outlook, asked which year I wanted, and listed matching emails from the Inbox:
powershell
$namespace = $outlook.GetNamespace("MAPI")
$folder = $namespace.GetDefaultFolder(6) # Inbox
$startDate = Get-Date -Year $Year -Month 1 -Day 1
$endDate = Get-Date -Year $Year -Month 12 -Day 31 -Hour 23 -Minute 59 -Second 59
$filter = "[ReceivedTime] >= '$($startDate.ToString('g'))' AND [ReceivedTime] <= '$($endDate.ToString('g'))'"
$filteredItems = $folder.Items.Restrict($filter)Sample output (names and addresses invented, obviously):
ReceivedTime Sender Subject HasAttachments
------------ ------ ------- --------------
04/03/2019 09:12:00 Alex Morgan Re: Project kickoff notes True
17/07/2019 14:45:00 Sam Whitfield Invoice #4471 False
02/11/2019 11:03:00 Jordan Lee Quarterly review draft TrueGood enough to prove the concept.
"Wait, which account?"
I have multiple mailboxes set up in Outlook, and the first version quietly grabbed whichever one happened to be Outlook's default — not necessarily the one I meant. That got fixed with a -ListAccounts switch to show every mailbox in the profile, and an -AccountName parameter to target the right one explicitly.
The COM error rabbit hole
This is where the "quick" in "quick and dirty" took a hit. Running the script threw:
Creating an instance of the COM component ... failed ...
RPC_E_CALL_REJECTEDTurned out I'd launched PowerShell ISE as Administrator, while Outlook was running as a normal user — Windows won't let COM calls cross that privilege boundary. Closing the elevated session and running it normally fixed that one.
Then came a second, gnarlier error:
Unable to cast COM object ... TYPE_E_CANTLOADLIBRARYA corrupted Office type-library registration, most likely left over from an update. An Office Quick Repair sorted it out.
"Why is 2016 missing?"
Once the script was actually working, the next surprise was that older years simply weren't showing up — even though I knew that mail existed. This turned into the most interesting bit of troubleshooting: Outlook's Cached Exchange Mode only keeps a rolling window of mail (often as little as 12 months) downloaded locally. The script can only see what's physically on disk, so anything outside that window is invisible to it, no matter how the query is written.
To make things more interesting, I discovered I was actually running the new, WebView2-based Outlook client — which doesn't expose the classic COM automation model at all, and doesn't have the traditional "mail to keep offline" setting either. The script had been quietly talking to a separate, hidden classic Outlook engine installed alongside it. Finding that, launching it directly, setting its sync window to the maximum, and letting it fully resync eventually surfaced the older mail.
Lesson learned: half the difficulty in a job like this isn't the code, it's the platform underneath it having more moving parts than you'd expect.
Sorting it for real
With the data actually available, the last piece was the point of the whole exercise — moving matched emails into year-named subfolders:
powershell
$destFolder = $folder.Folders | Where-Object { $_.Name -eq $yearFolderName }
if (-not $destFolder) {
$destFolder = $folder.Folders.Add($yearFolderName)
}
foreach ($mail in $mailObjects) {
$mail.Move($destFolder) | Out-Null
}With a confirmation prompt before anything actually moved, and a -Force switch to skip it once I trusted the process:
powershell
.\List-EmailsByYear.ps1 -Year 2015 -AccountName "your-account-name" -MoveToYearFolderAnd once I was moving a few thousand emails at a time, a progress bar earned its place too — nice to actually see it working through a big batch rather than staring at a frozen-looking console window.
What I'd Take Away From This
Match the tool to the job. A proper connector-based integration is the right call for something recurring or shared. For a single, personal cleanup job, a rough-and-ready script was genuinely the faster, more sensible route — and there's no shame in "quick and dirty" when that's actually the correct tool for the scale of the problem.
The API is bigger than the one thing you're using it for. Once you're talking to Outlook's object model, moving emails is just the start — the same approach extends to categorising, flagging, exporting attachments, working with contacts and calendars, and more.
Expect the platform to be the hard part, not the code. Every genuinely tricky moment in this project was environmental — permissions, cached data limits, two versions of Outlook coexisting on the same machine — not the scripting itself.
AI-generated doesn't mean run-without-reading. Claude wrote every version of this script, and it saved me a lot of time — but I still read through each iteration before pointing it at my real inbox. A script that can move or delete thousands of emails deserves a proper look first, regardless of who or what wrote it.
Forty thousand emails, fourteen years, and a script that's maybe 150 lines long. Sometimes the quick and dirty option really is the right one.