Public/Remove-FslOrphanedOst.ps1
|
function Remove-FslOrphanedOst { <# .SYNOPSIS Deletes orphaned Outlook OST cache files from a profile - the explicit, WhatIf-first cleanup companion to FSLogix event 29 ('Orphaned OST file(s) found'). .DESCRIPTION OST files are regenerable local caches of an Exchange mailbox; orphaned ones (left behind by profile repairs, mailbox migrations or cached-mode changes) only bloat the profile container. This command keeps the most recently written OST file(s) per folder as the active cache and deletes the rest. Files that are locked (in use by Outlook) are skipped with a warning, never forced. This is the first of the explicit -Fix companions: it is NEVER called by Invoke-FslDiagnostic, honors -WhatIf/-Confirm (ConfirmImpact High) and reports exactly what it did per file. .PARAMETER Path Profile root (a live C:\Users\<user> folder while the user is signed in, or the root of a mounted container) or the Outlook cache folder itself. When the path itself holds no OST files, the well-known AppData\Local\Microsoft\Outlook subfolder is used. .PARAMETER KeepNewest How many of the most recently written OST files to keep per folder. Defaults to 1 (the active cache). .EXAMPLE Remove-FslOrphanedOst -Path C:\Users\jdoe -WhatIf Shows which OST files would be deleted, touches nothing. .EXAMPLE Remove-FslOrphanedOst -Path X:\ -Confirm:$false Cleans a container mounted as X:\ (user signed out) without prompting. #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType([pscustomobject])] param( [Parameter(Mandatory)] [string]$Path, [ValidateRange(1, 10)] [int]$KeepNewest = 1 ) if (-not (Test-Path -LiteralPath $Path)) { Write-Warning "Path '$Path' not found." return } $outlookPath = $Path if (@(Get-ChildItem -LiteralPath $outlookPath -Filter '*.ost' -File -ErrorAction SilentlyContinue).Count -eq 0) { $candidate = Join-Path $Path 'AppData\Local\Microsoft\Outlook' if (Test-Path -LiteralPath $candidate) { $outlookPath = $candidate } } $ostFiles = @(Get-ChildItem -LiteralPath $outlookPath -Filter '*.ost' -File -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending) if ($ostFiles.Count -eq 0) { Write-Warning ("No OST files found under '{0}'." -f $outlookPath) return } $orphans = @($ostFiles | Select-Object -Skip $KeepNewest) if ($orphans.Count -eq 0) { Write-Verbose ("Only {0} OST file(s) present - nothing to clean with KeepNewest={1}." -f $ostFiles.Count, $KeepNewest) return } foreach ($file in $orphans) { $sizeMB = [math]::Round($file.Length / 1MB, 1) $removed = $false $reason = '' if ($PSCmdlet.ShouldProcess($file.FullName, ("Delete orphaned OST ({0} MB)" -f $sizeMB))) { try { Remove-Item -LiteralPath $file.FullName -Force -ErrorAction Stop $removed = $true } catch { $reason = ("In use or not deletable: {0}" -f $_.Exception.Message) Write-Warning ("Skipped '{0}': {1}" -f $file.Name, $reason) } } else { $reason = 'Skipped (WhatIf or confirmation declined)' } [pscustomobject]@{ PSTypeName = 'FSLogixDoctor.OstCleanup' File = $file.FullName SizeMB = $sizeMB LastWrite = $file.LastWriteTime Removed = $removed Reason = $reason } } } |