Hello. I am going to be posting a few scripts that I have been working on to help run some reports. Hopefully, maybe these can get moved over to MS Graph as I get better at that module set. Anyway, this script will get all the User Mailboxes in the M365 tenant, check to see if it is 80 percent full and report on those mailboxes in a CSV file, no matter what EXO License plan they have.
This is helpful if you’re making a case for Archive Mailboxes or you have multiple users complaining of their mailboxes getting full. Please review this script and provide any suggestions you may have for error handling or making the script better. I always welcome suggestions!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | <# .SYNOPSIS Get a report of all mailboxes in your M365 tenant that are 80% full. .NOTES Name: Get-HighUsageMailboxes.ps1 Author: Lance Lingerfelt / lance.lingerfelt@ldlnet.net Version: 1.0 Modify Date: 2023-08-14 #> #Define Report Path and Parameters [CmdletBinding()] Param( [Parameter(Mandatory = $false)] [string] $ReportsPath = ".\Reports", [Parameter(Mandatory = $false)] [float] $Threshold = 80 ) # ================================================ # DO NOT MODIFY BEGIN # ================================================ $ErrorActionPreference = 'SilentlyContinue' $Date = Get-Date -Format "MM/dd/yyyy" # Set Logging Configuration $Log = [PSCustomObject]@{ Path = "C:\Temp\Logs\Get-HighUsageMailboxes" Name = "$($Date).log" } # ================================================ # DO NOT MODIFY END # ================================================ # ================================================ # SCRIPT BEGIN # ================================================ # Create New Logger Instance if Enabled if ($PSCmdlet.ShouldProcess("Create New Logger Instance", $Log.Path)) { # Import Logger Module try { if ( -not (Get-Module -Name PoShLog -ListAvailable) ) { Install-Module -Name PoShLog -Scope CurrentUser -Force } else { Import-Module -Name PoShLog -Force } } catch { Write-Host -Object "Unable to import logger module. Error: $($_.Exception.Message)" exit 1 } # Create New Logger Instance. Verbose logging level. Log to file and console. Start Logger. New-Logger | ` Set-MinimumLevel -Value Verbose | ` Add-SinkFile -Path "$($Log.Path)\$($Log.Name)" -OutputTemplate ` '{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}' -RollingInterval Day | ` Add-SinkConsole | ` Start-Logger # Log Start of Script Write-VerboseLog "Start of Script." } if ($PSCmdlet.ShouldProcess("Create New Exchange Online Instance", $Log.Path)) { # Import ExchangeOnlineManagement Module try { if ( -not (Get-Module -Name ExchangeOnlineManagement -ListAvailable) ) { Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser -Force } else { Import-Module -Name ExchangeOnlineManagement -Force } } catch { Write-Host -Object "Unable to import ExchangeOnlineManagement module. Error: $($_.Exception.Message)" exit 1 } } # Connect to Exchange Online Connect-ExchangeOnline -ShowBanner:$false #Export Array $exportresults = @() # Get all user mailboxes and their usage statistics $Mailboxes = Get-Mailbox -ResultSize Unlimited $MailboxCount = $Mailboxes.count $i = 0 #Loop through each result and create custom Values foreach ($Mailbox in $Mailboxes) { $i++ Write-Host "Evaluating [$($Mailbox.DisplayName)] Mailbox, Number [$i] of [$MailboxCount]" $MailboxStats = $Mailbox.UserPrincipalName | Get-MailboxStatistics [float]$MailboxTotalUsage = [math]::Round($($MailboxStats.TotalItemSize.Value.ToString().Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) [float]$MailboxQuota = $Mailbox.ProhibitSendReceiveQuota.ToString().Split(" ")[0] [float]$UsagePercentage = $MailboxTotalUsage / $MailboxQuota * 100 if ($UsagePercentage -gt $Threshold) { Write-Host "Found a large mailbox for user [$($Mailbox.DisplayName)] where usage percentage is [$UsagePercentage]" -ForegroundColor Yellow Write-VerboseLog "Found a large mailbox for user [$($Mailbox.DisplayName)] where usage percentage is [$UsagePercentage]" #Create the custom Objects for the Array and add them $exportresults += [PSCustomObject]@{ User = $Mailbox.DisplayName TotalMailboxSize = $MailboxTotalUsage Quota = $MailboxQuota UsagePercentage = $UsagePercentage } } } #Create Report Path if not there if (Test-Path $ReportsPath) { #Do Nothing } else { New-Item -Type Directory -Path $ReportsPath } #Export the Results $exportresults | Export-Csv $ReportsPath\HighUsageMailboxes-$(Get-Date -Format yyyyMMddThhmmss).csv -notypeinformation # Disconnect from Exchange Online Disconnect-ExchangeOnline -Confirm:$false # ================================================ # SCRIPT END # ================================================ Write-VerboseLog "End of Script. Exiting!" |
The script will output a CSV file that will look like this:
(NOTE: Values are in GB per the script)
Conclusion: If you need a report to show mailboxes nearing their quota without it being too close, then this script is for you. Thanks again!!!!
About Lance Lingerfelt
Lance Lingerfelt is an M365 Specialist and Evangelist with over 20 years of experience in the Information Technology field. Having worked in enterprise environments to small businesses, he is able to adapt and provide the best IT Training and Consultation possible. With a focus on AI, the M365 Stack, and Healthcare, he continues to give back to the community with training, public speaking events, and this blog.