This PowerShell script manages large Microsoft 365 mailbox folders by splitting them into smaller ones with no more than 99,999 emails each. It uses the Microsoft Graph API to authenticate the user, fetch mailbox folders, and handle operations like creating new folders and moving emails in batches. Key features include:
- Logging: Tracks every step of the process, including successes, errors, and retries, to a log file.
- Error Handling and Retry Logic: Ensures robust execution with multiple attempts for each operation.
- Batch Processing: Moves emails in batches of 99,999 to newly created folders, minimizing API call limits.
- Interactive Confirmation: Asks the user for confirmation before performing the split.
The script ensures mailbox organization and avoids performance issues caused by excessively large folders.
# Install the Microsoft Graph PowerShell module if it's not installed
Install-Module Microsoft.Graph -Scope CurrentUser -Force -AllowClobber
# Function to write log to the file
function Write-Log {
param (
[string]$message,
[string]$logFileName
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logMessage = "$timestamp - $message"
$logMessage | Out-File -FilePath $logFileName -Append
Write-Host $logMessage # Also output to console
}
# Step 1: Authenticate the user using device code flow
Write-Host "Please authenticate to Microsoft Graph..."
$logFileName = "Mailbox_Split_Log.txt"
# Log the start of the process
Write-Log "Starting the mailbox split operation..." $logFileName
# Authenticate the user
Connect-MgGraph -Scopes "Mail.ReadWrite", "Mail.Move", "MailFolders.ReadWrite"
# Confirm authentication
$me = Get-MgUser -UserId "me"
Write-Log "Authenticated as: $($me.DisplayName) ($($me.UserPrincipalName))" $logFileName
# Step 2: Get the current user's mailbox folders
Write-Log "Fetching folders for $($me.UserPrincipalName)" $logFileName
$folders = Get-MgUserMailFolder -UserId "me" -All
# Step 3: Analyze and summarize folders with more than 99,999 items
$largeFolders = $folders | Where-Object { $_.TotalItemCount -gt 99999 }
# Display summary of folders to be split
foreach ($folder in $largeFolders) {
$totalItems = $folder.TotalItemCount
$excessItems = $totalItems - 99999
$numNewFolders = [math]::Ceiling($excessItems / 99999)
Write-Log "Folder: $($folder.DisplayName) (ID: $($folder.Id))" $logFileName
Write-Log "Total Items: $totalItems" $logFileName
Write-Log "Items to be moved: $excessItems" $logFileName
Write-Log "Proposed new folders: $numNewFolders" $logFileName
}
# Step 4: Confirm before proceeding
$confirmation = Read-Host -Prompt "Do you want to proceed with splitting the folders? (yes/no)"
if ($confirmation -ne "yes") {
Write-Log "Operation canceled by user." $logFileName
Write-Host "Operation canceled."
Exit
}
# Error handling function for retries with logging
function Retry-Operation {
param (
[scriptblock]$ScriptBlock,
[int]$MaxRetries = 3,
[int]$RetryDelay = 5, # Seconds to wait between retries
[string]$logFileName
)
$attempt = 0
while ($attempt -lt $MaxRetries) {
try {
$attempt++
Write-Log "Attempt $attempt to execute operation." $logFileName
& $ScriptBlock
Write-Log "Operation succeeded on attempt $attempt." $logFileName
return # Success, exit the retry loop
} catch {
Write-Log "Attempt $attempt failed: $($_.Exception.Message)" $logFileName
# Log the entire error details (stack trace, message, etc.)
Write-Log "Error Details: $($_ | Out-String)" $logFileName
if ($attempt -eq $MaxRetries) {
$errorMsg = "Max retries reached. Operation failed."
Write-Log $errorMsg $logFileName
throw $errorMsg
}
Write-Log "Retrying in $RetryDelay seconds..." $logFileName
Start-Sleep -Seconds $RetryDelay
}
}
}
# Function to create a new folder with retry logic and logging
function Create-NewMailFolder {
param (
[string]$parentFolderId,
[string]$folderName,
[string]$logFileName
)
Retry-Operation -ScriptBlock {
$newFolder = New-MgUserMailFolder -UserId "me" -DisplayName $folderName -ParentFolderId $parentFolderId
Write-Log "Created new folder $folderName with ID $($newFolder.Id)" $logFileName
return $newFolder.Id
} -logFileName $logFileName
}
# Function to move emails in batches with retry logic and logging
function Move-EmailsInBatches {
param (
[string]$sourceFolderId,
[string]$destinationFolderId,
[int]$batchSize = 99999,
[string]$logFileName
)
Retry-Operation -ScriptBlock {
# Get messages in the source folder
$messages = Get-MgUserMailFolderMessage -UserId "me" -MailFolderId $sourceFolderId -Top $batchSize
foreach ($message in $messages) {
$messageId = $message.Id
# Move the message
Retry-Operation -ScriptBlock {
Move-MgUserMessage -UserId "me" -MessageId $messageId -DestinationId $destinationFolderId
Write-Log "Moved message $messageId to folder $destinationFolderId" $logFileName
} -logFileName $logFileName
}
Write-Log "Moved $batchSize messages from folder $sourceFolderId to folder $destinationFolderId" $logFileName
} -logFileName $logFileName
}
# Process each large folder with error handling, retries, and logging
foreach ($folder in $largeFolders) {
$folderId = $folder.Id
$totalItems = $folder.TotalItemCount
$excessItems = $totalItems - 99999
if ($excessItems -gt 0) {
$numNewFolders = [math]::Ceiling($excessItems / 99999)
# Create the necessary number of new folders and move items
for ($i = 1; $i -le $numNewFolders; $i++) {
$newFolderName = "split_${folderId}_$i"
$newFolderId = Create-NewMailFolder -parentFolderId $folderId -folderName $newFolderName -logFileName $logFileName
# Move emails to the new folder
if ($i -lt $numNewFolders) {
# Move a full batch of 99,999 emails
Move-EmailsInBatches -sourceFolderId $folderId -destinationFolderId $newFolderId -batchSize 99999 -logFileName $logFileName
} else {
# Move the remainder (less than 99,999 emails)
$remainder = $excessItems % 99999
Move-EmailsInBatches -sourceFolderId $folderId -destinationFolderId $newFolderId -batchSize $remainder -logFileName $logFileName
}
}
}
}
Write-Log "All folder splits and moves for $($me.UserPrincipalName) are completed!" $logFileName
Write-Host "All folder splits and moves are completed!"
@echo off
powershell.exe -ExecutionPolicy Bypass -File "C:\Path\To\SplitMailbox.ps1"
pause
Sorry! The Author has not filled his profile.