Make interactive
This commit is contained in:
@ -1,44 +1,195 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Count the number of received emails in a Microsoft 365 mailbox during a specified time window.
|
||||
Interactive mailbox message counter for Microsoft 365 (Graph).
|
||||
|
||||
.DESCRIPTION
|
||||
This script queries Exchange Online via Microsoft Graph to return the total count of emails
|
||||
received by a specific mailbox within a given date range. It searches all folders in the mailbox
|
||||
(including Inbox, subfolders, Junk, and Deleted Items), ensuring that emails moved after delivery
|
||||
are still included in the count. Drafts are excluded, and self-sent messages from the mailbox
|
||||
owner are not counted as "received."
|
||||
Prompts the user to choose "my mailbox" or "another/shared mailbox", gathers the target mailbox,
|
||||
start date, and end date, then connects to Microsoft Graph with the appropriate delegated scope
|
||||
and returns a count of RECEIVED messages (excluding drafts and self-sent) within the specified
|
||||
local time window. It searches ALL folders (Inbox, subfolders, Junk, Deleted Items, etc.) so
|
||||
post-delivery moves don't matter.
|
||||
|
||||
Date boundaries are applied using local time (Eastern Time in this example).
|
||||
Adjust offsets (-05:00 / -04:00) for daylight savings or your region.
|
||||
Time boundaries are created using the specified Windows time zone (default: "Eastern Standard Time"
|
||||
for Toronto). The script computes the correct UTC offset for the provided dates (including DST)
|
||||
and generates ISO 8601 timestamps with offsets (e.g., 2025-01-01T00:00:00-05:00).
|
||||
|
||||
.PARAMETER $u
|
||||
The target user's email address. Example: 'f@d.org'
|
||||
.REQUIREMENTS
|
||||
- PowerShell 5.1+ (Windows) or PowerShell 7+
|
||||
- Microsoft.Graph PowerShell SDK (will auto-install for CurrentUser if missing)
|
||||
- For "another/shared mailbox":
|
||||
* Your signed-in account must have Full Access to that mailbox in Exchange Online.
|
||||
|
||||
.PARAMETER $start
|
||||
The inclusive start of the time window in ISO 8601 format with time zone offset.
|
||||
Example: '2025-01-01T00:00:00-05:00' for January 1st, 2025, 12:00 AM ET.
|
||||
|
||||
.PARAMETER $end
|
||||
The exclusive end of the time window in ISO 8601 format with time zone offset.
|
||||
Example: '2025-02-01T00:00:00-05:00' for February 1st, 2025, 12:00 AM ET.
|
||||
.PARAMETERS (prompted)
|
||||
- Scope mode: "1 = My mailbox" or "2 = Another/shared mailbox"
|
||||
- Mailbox SMTP address (e.g., user@domain.com)
|
||||
- Start date (YYYY-MM-DD), inclusive
|
||||
- End date (YYYY-MM-DD), exclusive
|
||||
|
||||
.OUTPUTS
|
||||
Returns a single integer representing the count of received messages.
|
||||
Prints a single integer count and a short summary of inputs.
|
||||
|
||||
.NOTES
|
||||
Requirements:
|
||||
- Microsoft.Graph PowerShell module
|
||||
- Connect-MgGraph with Mail.Read delegated permission (if signed in as the user)
|
||||
OR Mail.Read application permission (with admin consent) if run as a service.
|
||||
|
||||
Install module: Install-Module Microsoft.Graph
|
||||
|
||||
Examples to connect:
|
||||
This is your mailbox: Connect-MgGraph -Scopes Mail.Read
|
||||
This is a shared mailbox you are a "Full" delegate of: Connect-MgGraph -Scopes Mail.Read.Shared
|
||||
Example month window:
|
||||
Start: 2025-01-01
|
||||
End: 2025-02-01
|
||||
These will be interpreted at 00:00 local time of the configured time zone.
|
||||
|
||||
(c) 2025 Robbie Ferguson. All rights reserved.
|
||||
#>
|
||||
|
||||
$u='mailbox@domain.com';$start='2025-01-01T00:00:00-05:00';$end='2025-02-01T00:00:00-05:00';$c=0; Get-MgUserMessage -UserId $u -Filter "receivedDateTime ge $start and receivedDateTime lt $end and isDraft eq false and (from/emailAddress/address ne '$u')" -CountVariable c -PageSize 1 | Out-Null; $c
|
||||
# --------------------------- User-editable settings ---------------------------
|
||||
# Windows Time Zone ID (use `tzutil /l` to list). "Eastern Standard Time" covers Toronto with DST.
|
||||
$TimeZoneId = 'Eastern Standard Time'
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Helper: ensure Microsoft Graph SDK is available
|
||||
function Ensure-GraphModule {
|
||||
$moduleName = 'Microsoft.Graph'
|
||||
if (-not (Get-Module -ListAvailable -Name $moduleName)) {
|
||||
Write-Host "Microsoft.Graph module not found. Installing for CurrentUser..."
|
||||
try {
|
||||
Set-PSRepository PSGallery -InstallationPolicy Trusted -ErrorAction SilentlyContinue
|
||||
Install-Module $moduleName -Scope CurrentUser -Force -ErrorAction Stop
|
||||
} catch {
|
||||
Write-Error "Failed to install Microsoft.Graph: $($_.Exception.Message)"
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
Import-Module $moduleName -ErrorAction Stop
|
||||
}
|
||||
|
||||
# Helper: get TimeZoneInfo safely
|
||||
function Get-TimeZoneInfoSafe {
|
||||
param([string]$Id)
|
||||
try {
|
||||
return [System.TimeZoneInfo]::FindSystemTimeZoneById($Id)
|
||||
} catch {
|
||||
Write-Error "Time zone '$Id' not found. Edit `$TimeZoneId at the top (Windows TZ IDs, e.g., 'Eastern Standard Time')."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Helper: build ISO timestamp with correct offset for a date at 00:00 local time
|
||||
function Get-ISOStartOfDayWithOffset {
|
||||
param(
|
||||
[datetime]$DateOnly,
|
||||
[System.TimeZoneInfo]$Tz
|
||||
)
|
||||
# Local 00:00 in the TZ
|
||||
$local = Get-Date -Date "$($DateOnly.ToString('yyyy-MM-dd')) 00:00:00"
|
||||
$offset = $Tz.GetUtcOffset($local)
|
||||
$sign = if ($offset -lt [TimeSpan]::Zero) { '-' } else { '+' }
|
||||
$hh = [math]::Abs($offset.Hours).ToString('00')
|
||||
$mm = [math]::Abs($offset.Minutes).ToString('00')
|
||||
$offStr = "$sign$hh`:$mm"
|
||||
return "{0}T00:00:00{1}" -f $local.ToString('yyyy-MM-dd'), $offStr
|
||||
}
|
||||
|
||||
# Helper: connect to Graph with appropriate scope
|
||||
function Connect-GraphWithScope {
|
||||
param([string]$Scope)
|
||||
try {
|
||||
Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
|
||||
} catch {}
|
||||
try {
|
||||
Connect-MgGraph -Scopes $Scope -ErrorAction Stop | Out-Null
|
||||
} catch {
|
||||
Write-Error "Failed to connect to Microsoft Graph with scope '$Scope': $($_.Exception.Message)"
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
# Helper: validate date input (YYYY-MM-DD)
|
||||
function Read-DateStrict {
|
||||
param([string]$Prompt,[datetime]$Default)
|
||||
while ($true) {
|
||||
$in = Read-Host "$Prompt (YYYY-MM-DD) [Enter for $($Default.ToString('yyyy-MM-dd'))]"
|
||||
if ([string]::IsNullOrWhiteSpace($in)) { return $Default.Date }
|
||||
if ([datetime]::TryParseExact($in, 'yyyy-MM-dd', $null, 'None', [ref]([datetime]$out))) {
|
||||
return $out.Date
|
||||
}
|
||||
Write-Host "Invalid date. Please use YYYY-MM-DD." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# Start
|
||||
Write-Host "=== M365 Mailbox Message Counter ===" -ForegroundColor Cyan
|
||||
Ensure-GraphModule
|
||||
|
||||
# 1) Which mailbox type?
|
||||
Write-Host "Select mailbox type:" -ForegroundColor Cyan
|
||||
Write-Host " 1) My mailbox"
|
||||
Write-Host " 2) Another/shared mailbox (requires delegation with Full Access)"
|
||||
$mode = Read-Host "Enter 1 or 2"
|
||||
if ($mode -notin @('1','2')) {
|
||||
Write-Error "Invalid selection."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 2) Target mailbox SMTP
|
||||
$Mailbox = Read-Host "Enter the mailbox SMTP address to check (e.g., user@domain.com)"
|
||||
if ([string]::IsNullOrWhiteSpace($Mailbox) -or ($Mailbox -notmatch '^[^@\s]+@[^@\s]+\.[^@\s]+$')) {
|
||||
Write-Error "Invalid email address."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 3) Date range (inclusive start, exclusive end)
|
||||
# Defaults to January 2025 for convenience; change as needed
|
||||
$defaultStart = Get-Date '2025-01-01'
|
||||
$defaultEnd = Get-Date '2025-02-01'
|
||||
$StartDate = Read-DateStrict -Prompt "Start date (inclusive)" -Default $defaultStart
|
||||
$EndDate = Read-DateStrict -Prompt "End date (exclusive)" -Default $defaultEnd
|
||||
if ($EndDate -le $StartDate) {
|
||||
Write-Error "End date must be AFTER start date."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Prepare TZ and ISO strings with correct offsets for each boundary date
|
||||
$tz = Get-TimeZoneInfoSafe -Id $TimeZoneId
|
||||
$startISO = Get-ISOStartOfDayWithOffset -DateOnly $StartDate -Tz $tz
|
||||
$endISO = Get-ISOStartOfDayWithOffset -DateOnly $EndDate -Tz $tz
|
||||
|
||||
# 4) Connect with proper scope
|
||||
$scope = if ($mode -eq '1') { 'Mail.Read' } else { 'Mail.Read.Shared' }
|
||||
Connect-GraphWithScope -Scope $scope
|
||||
|
||||
# Show who is signed in (helpful for multi-tenant admins)
|
||||
try {
|
||||
$ctx = Get-MgContext
|
||||
if ($ctx) { Write-Host "Connected as: $($ctx.Account) | Tenant: $($ctx.TenantId) | Scope(s): $($ctx.Scopes -join ', ')" -ForegroundColor DarkGray }
|
||||
} catch {}
|
||||
|
||||
# 5) Perform count
|
||||
try {
|
||||
$count = 0
|
||||
# Exclude drafts and self-sent (from == mailbox) to count RECEIVED mail
|
||||
$filter = "receivedDateTime ge $startISO and receivedDateTime lt $endISO and isDraft eq false and (from/emailAddress/address ne '$Mailbox')"
|
||||
# Ask Graph for a count; we don't need to page through items (PageSize 1 trick)
|
||||
Get-MgUserMessage -UserId $Mailbox -Filter $filter -CountVariable count -PageSize 1 | Out-Null
|
||||
|
||||
Write-Host ""
|
||||
Write-Host "Mailbox: $Mailbox" -ForegroundColor Green
|
||||
Write-Host "Window: $($StartDate.ToString('yyyy-MM-dd')) to $($EndDate.ToString('yyyy-MM-dd')) ($TimeZoneId)" -ForegroundColor Green
|
||||
Write-Host "Count of RECEIVED messages (all folders, excluding drafts & self-sent):" -ForegroundColor Green
|
||||
Write-Host $count -ForegroundColor Cyan
|
||||
} catch {
|
||||
Write-Host ""
|
||||
Write-Error "Query failed: $($_.Exception.Message)"
|
||||
|
||||
# Common guidance
|
||||
if ($mode -eq '2') {
|
||||
Write-Host "If this is another/shared mailbox, ensure:" -ForegroundColor Yellow
|
||||
Write-Host " - Your account has Full Access to $Mailbox in Exchange Online (Mailbox Delegation)." -ForegroundColor Yellow
|
||||
} else {
|
||||
Write-Host "If this is your own mailbox and it still fails, try reconnecting and consenting the scope." -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ""
|
||||
Read-Host "Press Enter to exit"
|
||||
|
||||
Reference in New Issue
Block a user