> ## Content Index
> Fetch the complete content index at: https://seanshares.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Find Oversized Outlook Data Files with PowerShell
- URL: https://seanshares.com/automation/outlook-data-file-report-powershell/
- Published: 2026-11-10T04:03:33.000Z
- Updated: 2026-11-10T04:03:33.000Z
- Description: Slow Outlook is often an oversized data file nobody has checked. This free PowerShell report finds every .ost and .pst on a PC, shows their sizes and flags the big ones.
- Author: Sean Shares
- Tags: Automation, #user, #admin

Slow Outlook is one of the most common Outlook complaints, and one of the most commonly misdiagnosed. People blame the internet connection, the computer's age, the number of browser tabs open, anything except the actual culprit sitting quietly in a folder most users have never opened. The real answer is very often an offline cache file that has grown to a size Outlook was never comfortable handling well, sitting on a machine where nobody has ever checked.

A single oversized .ost file, built up over years of a mailbox with a generous retention policy, is enough on its own to make Outlook take over a minute to open, make search unusable, and add a visible lag to every folder switch. Once identified, the fix is usually straightforward. Finding it is the slow part, because there is no built-in Windows dialog that just tells you "here is your Outlook data file and here is how big it is." Locating it requires knowing where to look, and the answer depends on whether the account uses a default profile location, a custom one, or an old-style .pst archive tucked away in Documents.

This script exists to skip that hunt entirely. It checks every location Outlook data files commonly end up, lists what it finds with size and last-modified date, and flags anything that has crossed a size threshold worth paying attention to. It is a reasonable first step whenever Outlook starts feeling slow, before touching anything else.

## Prerequisites

This is one of the simpler scripts in the collection, with requirements that amount to almost nothing beyond a working Windows install.

- Windows PowerShell 5.1, built into Windows 10 and 11
- Outlook installed and configured with at least one account, though the script runs fine even without Outlook if you just want to check for leftover data files
- No admin rights required, since it only reads from folders already inside the current user's own profile
- No PowerShell modules and no connection to your tenant

Because everything here is a local file system read, this works identically whether the account is Exchange Online, on-premises Exchange, or even a POP/IMAP account using a local .pst as its primary store.

## The Script

```
<#
.SYNOPSIS
    Get-OutlookMailboxReport - GUI report of local Outlook data files (.ost/.pst).
.DESCRIPTION
    Lists the Outlook offline cache (.ost) and personal archive (.pst) files on
    the PC with their sizes, locations, and last-modified dates. Oversized OST
    files are a common cause of slow Outlook; this report makes them easy to
    spot. Large files (>40 GB) are highlighted.

    Read-only. Saves an HTML report to the Desktop. PowerShell 5.1+, no modules.
.NOTES
    Run: right-click > Run with PowerShell.
#>

if ([System.Threading.Thread]::CurrentThread.ApartmentState -ne 'STA') {
    Start-Process powershell.exe -ArgumentList @('-NoProfile','-ExecutionPolicy','Bypass','-STA','-File',"`"$PSCommandPath`"")
    return
}
Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing
[System.Windows.Forms.Application]::EnableVisualStyles()

$desktop=[Environment]::GetFolderPath('Desktop')
$reportPath=Join-Path $desktop ("OutlookMailbox-$env:USERNAME-$(Get-Date -Format 'yyyyMMdd-HHmmss').html")
function Enc { param($s) if($null -eq $s){return ''}; return [System.Security.SecurityElement]::Escape([string]$s) }

$searchRoots = @(
    (Join-Path $env:LOCALAPPDATA 'Microsoft\Outlook'),
    (Join-Path $env:USERPROFILE 'Documents\Outlook Files'),
    (Join-Path $env:USERPROFILE 'Documents')
) | Select-Object -Unique

$form=New-Object System.Windows.Forms.Form
$form.Text='Outlook Mailbox / Data File Report'; $form.Size=New-Object System.Drawing.Size(560,420)
$form.StartPosition='CenterScreen'; $form.FormBorderStyle='FixedSingle'; $form.MaximizeBox=$false
$form.Font=New-Object System.Drawing.Font('Segoe UI',9)
$lbl=New-Object System.Windows.Forms.Label; $lbl.Text='Outlook Data File Report'
$lbl.Font=New-Object System.Drawing.Font('Segoe UI',13,[System.Drawing.FontStyle]::Bold)
$lbl.Location=New-Object System.Drawing.Point(15,12); $lbl.Size=New-Object System.Drawing.Size(520,28); $form.Controls.Add($lbl)
$txt=New-Object System.Windows.Forms.TextBox; $txt.Multiline=$true; $txt.ScrollBars='Vertical'; $txt.ReadOnly=$true
$txt.Location=New-Object System.Drawing.Point(15,52); $txt.Size=New-Object System.Drawing.Size(525,270)
$txt.Font=New-Object System.Drawing.Font('Consolas',9); $txt.BackColor=[System.Drawing.Color]::White; $form.Controls.Add($txt)
$btnGo=New-Object System.Windows.Forms.Button; $btnGo.Text='Generate Report'; $btnGo.Location=New-Object System.Drawing.Point(200,336); $btnGo.Size=New-Object System.Drawing.Size(120,34)
$btnGo.BackColor=[System.Drawing.Color]::FromArgb(15,108,189); $btnGo.ForeColor=[System.Drawing.Color]::White; $btnGo.FlatStyle='Flat'; $form.Controls.Add($btnGo)
$btnOpen=New-Object System.Windows.Forms.Button; $btnOpen.Text='Open Report'; $btnOpen.Location=New-Object System.Drawing.Point(325,336); $btnOpen.Size=New-Object System.Drawing.Size(110,34); $btnOpen.FlatStyle='Flat'; $btnOpen.Enabled=$false
$btnOpen.Add_Click({ Start-Process $reportPath }); $form.Controls.Add($btnOpen)
$btnClose=New-Object System.Windows.Forms.Button; $btnClose.Text='Close'; $btnClose.Location=New-Object System.Drawing.Point(440,336); $btnClose.Size=New-Object System.Drawing.Size(100,34); $btnClose.FlatStyle='Flat'
$btnClose.Add_Click({ $form.Close() }); $form.Controls.Add($btnClose)
$Log={ param($m) $txt.AppendText($m+[Environment]::NewLine); [System.Windows.Forms.Application]::DoEvents() }

$btnGo.Add_Click({
    $btnGo.Enabled=$false; $txt.Clear(); & $Log 'Searching for Outlook data files...'
    $files=@()
    foreach ($root in $searchRoots) {
        if (Test-Path -LiteralPath $root) {
            & $Log "  Scanning $root"
            $files += Get-ChildItem -LiteralPath $root -Include *.ost,*.pst -Recurse -Force -ErrorAction SilentlyContinue
        }
    }
    $files = @($files | Sort-Object FullName -Unique)
    $html=@"
<!DOCTYPE html><html><head><meta charset='utf-8'><title>Outlook Data File Report</title><style>
body{font-family:Segoe UI,Arial,sans-serif;margin:30px;color:#201f1e;background:#faf9f8}
h1{color:#0f6cbd;border-bottom:3px solid #0f6cbd;padding-bottom:8px}
table{border-collapse:collapse;width:100%;background:#fff;box-shadow:0 1px 3px rgba(0,0,0,.1)}
th{background:#0f6cbd;color:#fff;text-align:left;padding:10px}td{padding:8px 10px;border-bottom:1px solid #edebe9}
tr:nth-child(even){background:#f3f2f1}.meta{color:#605e5c;font-size:13px;margin-bottom:20px}.warn{color:#d83b01;font-weight:bold}
</style></head><body><h1>Outlook Data File Report</h1>
<div class='meta'>User: <b>$env:USERNAME</b> | Generated: <b>$(Get-Date)</b></div>
"@
    if ($files.Count -eq 0) {
        $html+='<p>No .ost or .pst files were found in the usual locations.</p>'
        & $Log '  No data files found.'
    } else {
        $total=0
        $html+='<table><tr><th>File</th><th>Type</th><th>Size (GB)</th><th>Last Modified</th><th>Location</th></tr>'
        foreach ($f in ($files | Sort-Object Length -Descending)) {
            $gb=[math]::Round($f.Length/1GB,2); $total+=$f.Length
            $type=if($f.Extension -eq '.ost'){'Offline cache'}else{'Personal/archive'}
            $cls=if($gb -ge 40){'warn'}else{''}
            $html+="<tr><td>$(Enc $f.Name)</td><td>$type</td><td class='$cls'>$gb</td><td>$($f.LastWriteTime)</td><td>$(Enc $f.DirectoryName)</td></tr>"
            & $Log "  $($f.Name): $gb GB"
        }
        $html+="</table><p><b>Total:</b> $([math]::Round($total/1GB,2)) GB across $($files.Count) file(s).</p>"
        $html+='<p style="color:#605e5c">Tip: An .ost over ~40 GB can slow Outlook. The offline cache rebuilds automatically if removed; .pst archives should be backed up before any change.</p>'
    }
    $html+='</body></html>'
    $html | Out-File -FilePath $reportPath -Encoding UTF8
    & $Log ''; & $Log "Report saved to Desktop: $([System.IO.Path]::GetFileName($reportPath))"
    $btnOpen.Enabled=$true; $btnGo.Enabled=$true
})
[void]$form.ShowDialog(); $form.Dispose()

```

## How It Works

The script searches three locations by default: `%LOCALAPPDATA%\Microsoft\Outlook`, which is where the offline cache (.ost) files live under a default installation, `Documents\Outlook Files`, the standard location Outlook suggests when a user creates a new .pst archive, and the root of the Documents folder itself, since plenty of users end up with a stray .pst sitting there from an old import or manual save. Searching all three catches the vast majority of real-world setups without requiring the user to know or report where their files actually live.

Within each location, **Get-ChildItem -Include \*.ost,\*.pst -Recurse** finds every matching file, and the results across all three search roots get combined and de-duplicated by full path before the report builds, since it is entirely possible for the same file to technically be reachable through more than one search root in an unusual folder configuration.

Each file gets classified by extension: .ost files are labeled "Offline cache," meaning they are Outlook's local copy of an Exchange or Microsoft 365 mailbox and can be safely deleted and rebuilt if needed, while .pst files are labeled "Personal/archive," meaning they are often the only copy of whatever data they contain and should be backed up rather than casually deleted. This distinction matters enormously for anyone acting on the report, since treating a .pst like a disposable cache file risks permanent data loss in a way that treating an .ost that way does not.

The size threshold is set at 40 GB, which is where Outlook's own performance guidance starts recommending users trim their mailbox or archive older mail. Any file at or above that size gets a distinct visual warning style in the report, so it stands out immediately in a list rather than requiring you to scan a column of numbers looking for the big one.

## Sample Output

![](https://seanshares.com/content/images/2026/11/Screenshot-2026-11-09-225155.png)

![](https://seanshares.com/content/images/2026/11/Screenshot-2026-11-09-225302.png)

## Why OST Files Grow So Large in the First Place

It is worth understanding why the offline cache balloons in size for some users and not others, since it changes how you think about a genuinely oversized .ost. In Cached Exchange Mode, which is the default and recommended setting for most Microsoft 365 accounts, Outlook keeps a local copy of everything in the mailbox so it can work offline and respond faster than round-tripping every action to the server. That local copy grows in direct proportion to how much mail, how many attachments, and how much calendar and contact data a user has accumulated.

Retention policies play a bigger role than most people expect. An organization with a generous or nonexistent auto-archive policy lets mail pile up indefinitely, and a user who never deletes anything, who keeps every attachment, every newsletter, every meeting invite going back a decade, ends up with a mailbox that can legitimately reach tens of gigabytes on the server side alone. The .ost simply mirrors whatever the server-side mailbox contains, so a large server mailbox always means a large local cache under Cached Exchange Mode.

There is a setting worth knowing about for genuinely oversized mailboxes: Outlook lets you configure Cached Exchange Mode to sync only a rolling window, such as the last 3, 6, or 12 months, rather than the entire mailbox. This keeps the local .ost bounded even as the server-side mailbox keeps growing, at the cost of needing an online connection to search or open older mail outside that window. For users who mostly care about recent mail and rarely dig into old messages, this is often a better long-term fix than repeatedly clearing an .ost that just grows right back to the same size.

## Gotchas & Safety

There is no **\-WhatIf** flag because this script does not touch, move, or delete anything. It searches for files and reads their metadata, then writes an HTML report. Nothing about running this affects Outlook, the mailbox, or any file it finds.

The one thing worth knowing: a large .ost is genuinely safe to delete if you need to, since Outlook rebuilds it automatically from the server the next time it starts, though the rebuild can take a while and requires a live connection to redownload everything. A large .pst is a different story entirely. It may be the only copy of years of archived mail, and there is no server-side original to rebuild it from. This report labels the two differently for exactly this reason, but the responsibility for treating them differently when acting on the report still falls on whoever is reading it.

Test this on a spare machine first if you plan to roll it out broadly, mainly to confirm the three default search paths match what your organization's users actually have, particularly if your environment uses custom Outlook profile locations or redirects Documents somewhere non-standard.

## Why Not Just Check Outlook's Own Mailbox Cleanup Tool?

Outlook does have a built-in Mailbox Cleanup feature under File, Tools, but it only reports on the currently open profile's mailbox and archive, one account at a time, and it does not surface .pst files that are not currently loaded into the active profile. A user with an old .pst they stopped opening years ago, still sitting on disk taking up space, would never see it flagged by Outlook's own tools, because Outlook only reports on what it currently has open. This script finds files on disk regardless of whether Outlook currently has them loaded, which is exactly the case that trips up the built-in tooling.

## FAQ & Variations

### Is it safe to just delete a large .ost file?

Yes, generally. The .ost is a local cache Outlook rebuilds automatically from the server. Close Outlook first, delete the file, then reopen Outlook and let it resync. Depending on mailbox size and connection speed, the resync can take anywhere from a few minutes to a few hours, so this is best done when the user does not need Outlook immediately afterward.

### Can I safely delete a .pst file the same way?

No, not without understanding what is inside it first. A .pst is often the only copy of its contents, particularly for older archived mail that has since been removed from the server. Always confirm the .pst is genuinely unneeded, or back it up somewhere safe, before deleting it.

### How do I change the 40 GB warning threshold?

Change the value in the `if ($gb -ge 40)` comparison inside the file loop to whatever threshold makes sense for your environment. Some organizations with tighter mailbox size limits might want to flag anything over 10 or 15 GB instead.

### Does this find data files stored on OneDrive or a network drive?

Only if they happen to fall under one of the three default search paths. If your organization stores .pst archives on a mapped network drive or a synced OneDrive folder outside those paths, add that path to the `$searchRoots` array near the top of the script.

### Why does the same file sometimes show up in multiple locations during testing?

It should not, since the script de-duplicates by full file path with `Sort-Object FullName -Unique` before building the report. If you genuinely see true duplicates, check whether two of the search roots overlap in your environment, for example if `Documents\Outlook Files` and `Documents` both resolve to paths that contain each other.

### Does this work for shared mailboxes as well as the primary account?

Yes, indirectly. Shared mailboxes that are added to a profile and cached offline get their own .ost files inside the same Outlook data folder, so they show up in the scan automatically alongside the primary account's cache file, with no special handling required.