Skip to content

Commit f53584b

Browse files
authored
Merge pull request #1019 from ojopiyo:patch-40
Create README.md
2 parents 6d49ccb + 2e4f932 commit f53584b

3 files changed

Lines changed: 264 additions & 0 deletions

File tree

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
# Exchange Online Mailboxes with Archive Disabled
2+
3+
## Summary
4+
5+
This PowerShell script performs an assessment of Exchange Online mailboxes to identify those that do not have an archive mailbox enabled. It uses certificate-based application authentication to connect securely to Exchange Online and retrieves mailbox information using the Exchange Online PowerShell module.
6+
7+
The script evaluates the `ArchiveStatus` property, identifies mailboxes where the status is `None`, and exports the results to a timestamped CSV report. It also generates a log file documenting the assessment lifecycle, mailbox retrieval count, findings, output locations, and any errors encountered.
8+
9+
The script is designed for Microsoft 365 administrative assessments, tenant configuration reviews, and ongoing mailbox archive compliance monitoring.
10+
11+
## Why It Matters
12+
13+
Exchange Online archive mailboxes provide additional storage capacity and support organisational email retention strategies. Mailboxes without an enabled archive may require investigation to determine whether their configuration aligns with business, operational, and compliance requirements.
14+
15+
For example, an M365 Administrator performing a tenant-wide storage assessment can use this script to identify mailboxes without an enabled archive, review the findings with service owners, and determine whether archive provisioning is required.
16+
17+
The report provides a practical starting point for remediation planning and configuration governance without making changes to mailbox settings.
18+
19+
### Benefits
20+
21+
- **Tenant-wide visibility:** Assesses mailboxes across the Exchange Online organisation.
22+
- **Configuration assessment:** Identifies mailboxes reporting an archive status of `None`.
23+
- **Audit-ready reporting:** Produces a timestamped CSV file suitable for filtering, analysis, and distribution.
24+
- **Operational traceability:** Records execution milestones, mailbox counts, results, and errors in a log file.
25+
- **Secure authentication:** Uses certificate-based application authentication rather than interactive user sign-in.
26+
- **Automation readiness:** Supports scheduled execution and integration into administrative assessment workflows.
27+
- **Non-invasive execution:** Performs a read-only assessment and does not enable, disable, or modify archive mailboxes.
28+
29+
## Prerequisites
30+
31+
- Exchange Online PowerShell module (`ExchangeOnlineManagement`)
32+
- Exchange Online application registration configured for certificate-based authentication
33+
- Valid certificate associated with the application
34+
- Appropriate Exchange Online application permissions and administrative configuration
35+
- Access to the certificate private key from the system executing the script
36+
37+
## Configuration
38+
39+
Update the following variables before execution:
40+
41+
| Variable | Description |
42+
| ----------------- | ---------------------------------------------------------- |
43+
| `$ClientID` | Azure AD Application (Client) ID |
44+
| `$ThumbPrint` | Certificate thumbprint |
45+
| `$Tenant` | Microsoft 365 tenant name (e.g. `contoso.onmicrosoft.com`) |
46+
| `$OutputFolder` | Currently set to (`C:\Temp\MailboxReport`) |
47+
48+
# [PnP PowerShell](#tab/pnpps)
49+
50+
```powershell
51+
52+
# ============================================================
53+
# ASSESSMENT: EXCHANGE ONLINE MAILBOXES WITH ARCHIVE DISABLED
54+
# ============================================================
55+
56+
$ClientID = "xxxxxxxxxxxxxxxxxxxxx"
57+
$ThumbPrint = "xxxxxxxxxxxxxxxxxxxxx"
58+
$Tenant = "contoso.onmicrosoft.com"
59+
60+
$OutputFolder = "C:\Temp\MailboxesWithArchiveDisabled"
61+
62+
if (-not (Test-Path -LiteralPath $OutputFolder)) {
63+
New-Item -Path $OutputFolder -ItemType Directory -Force | Out-Null
64+
}
65+
66+
$TimeStamp = Get-Date -Format "yyyyMMdd_HHmmss"
67+
$CsvPath = Join-Path $OutputFolder "MailboxesWithArchiveDisabled_$TimeStamp.csv"
68+
$LogPath = Join-Path $OutputFolder "MailboxesWithArchiveDisabled_$TimeStamp.log"
69+
70+
$Connected = $false
71+
72+
try {
73+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Assessment started."
74+
75+
# ======================================================================
76+
# CONNECT TO EXCHANGE ONLINE USING CERTIFICATE-BASED APP AUTHENTICATION
77+
# ======================================================================
78+
Import-Module ExchangeOnlineManagement -ErrorAction Stop
79+
80+
Connect-ExchangeOnline `
81+
-AppId $ClientID `
82+
-CertificateThumbprint $ThumbPrint `
83+
-Organization $Tenant `
84+
-ShowBanner:$false `
85+
-ErrorAction Stop
86+
87+
$Connected = $true
88+
89+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Connected to Exchange Online."
90+
# ======================================================================
91+
# RETRIEVE ALL MAILBOXES, INCLUDING THE ARCHIVESTATUS PROPERTY
92+
# ======================================================================
93+
$Mailboxes = @(
94+
Get-EXOMailbox `
95+
-ResultSize Unlimited `
96+
-Properties ArchiveStatus, RecipientTypeDetails `
97+
-ErrorAction Stop
98+
)
99+
100+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Retrieved $($Mailboxes.Count) mailboxes."
101+
102+
# ============================================================================
103+
# IDENTIFY MAILBOXES WITHOUT AN ENABLED ARCHIVE
104+
# ============================================================================
105+
$Results = @(
106+
$Mailboxes |
107+
Where-Object { $_.ArchiveStatus -eq "None" } |
108+
Select-Object `
109+
DisplayName,
110+
PrimarySmtpAddress,
111+
UserPrincipalName,
112+
RecipientTypeDetails,
113+
ArchiveStatus
114+
)
115+
# ======================================================================
116+
# EXPORT RESULTS, INCLUDING HEADERS WHEN NO MAILBOXES MATCH
117+
# ======================================================================
118+
if ($Results.Count -gt 0) {
119+
$Results | Export-Csv -LiteralPath $CsvPath -NoTypeInformation -Encoding UTF8
120+
}
121+
else {
122+
"DisplayName,PrimarySmtpAddress,UserPrincipalName,RecipientTypeDetails,ArchiveStatus" |
123+
Set-Content -LiteralPath $CsvPath -Encoding UTF8
124+
}
125+
126+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Mailboxes with archive disabled: $($Results.Count)."
127+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - CSV output: $CsvPath"
128+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Assessment completed successfully."
129+
130+
Write-Host "Assessment completed successfully." -ForegroundColor Green
131+
Write-Host "Total mailboxes assessed: $($Mailboxes.Count)"
132+
Write-Host "Mailboxes with archive disabled: $($Results.Count)"
133+
Write-Host "CSV report: $CsvPath"
134+
Write-Host "Log file: $LogPath"
135+
}
136+
catch {
137+
$ErrorMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - ERROR: $($_.Exception.Message)"
138+
Add-Content -LiteralPath $LogPath -Value $ErrorMessage
139+
Write-Error $ErrorMessage
140+
}
141+
finally {
142+
if ($Connected) {
143+
Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
144+
Add-Content -LiteralPath $LogPath -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - Disconnected from Exchange Online."
145+
}
146+
}
147+
148+
149+
```
150+
151+
## Usage
152+
153+
1. Configure $ClientID, $ThumbPrint, and $Tenant with the appropriate application and tenant details.
154+
2. Set $OutputFolder to a suitable location with sufficient storage and appropriate access controls.
155+
3. Run the script under an identity that can access the configured certificate and output directory.
156+
4. Review the generated CSV report and log file.
157+
158+
The script creates the output directory if it does not exist. Each execution generates timestamped output files to help preserve historical assessment results.
159+
160+
## Output
161+
162+
The script generates two files:
163+
164+
| File | Description |
165+
| -------------------------------------------------- | -------------------------------------------------------------------- |
166+
| `MailboxesWithArchiveDisabled_yyyyMMdd_HHmmss.csv` | Mailbox records where ArchiveStatus is `None` |
167+
| `MailboxesWithArchiveDisabled_yyyyMMdd_HHmmss.log` | Assessment progress, mailbox counts, output paths, and error details |
168+
169+
The CSV contains the following fields:
170+
171+
- `DisplayName`
172+
- `PrimarySmtpAddress`
173+
- `UserPrincipalName`
174+
- `RecipientTypeDetails`
175+
- `ArchiveStatus`
176+
177+
If no matching mailboxes are found, the script creates a CSV containing the column headers without any data rows.
178+
179+
## Notes
180+
181+
- **Assessment scope:** The report includes mailboxes returned by `Get-EXOMailbox -ResultSize Unlimited` for which `ArchiveStatus` is `None`. It should not be interpreted as a definitive inventory of every Exchange recipient type.
182+
- **Archive status:** A status of `None` indicates that an archive is not currently reported as enabled. Validate individual cases against organisational requirements before taking action.
183+
- **No remediation:** The script does not provision archives or change retention policies.
184+
- **Large tenants:** Retrieving all mailboxes into memory is appropriate for many assessments. For exceptionally large environments, consider implementing paged processing and incremental CSV exports.
185+
- **Error handling:** Errors are logged, and the Exchange Online session is disconnected when the connection was successfully established. For unattended automation, consider returning a non-zero process exit code when the assessment fails.
186+
- **Credential management:** Do not commit production certificate details, private keys, or other credentials to source control.
187+
- **Output protection:** Store reports in an appropriately secured location because mailbox identifiers and addresses may be operationally sensitive.
188+
189+
## Recommended Use
190+
191+
Run the assessment periodically as part of Exchange Online configuration reviews, archive provisioning assessments, and Microsoft 365 operational reporting. Use the results to support investigation and remediation decisions rather than treating the absence of an archive as an automatic configuration fault.
192+
193+
## Contributors
194+
195+
|Author(s)|
196+
|-----------|
197+
|[Josiah Opiyo](https://github.com/ojopiyo)|
198+
199+
*Built with a focus on automation, governance, least privilege, and clean Microsoft 365 tenants - helping M365 admins gain visibility and reduce operational risk.*
200+
201+
## Version history
202+
203+
|Version|Date|Comments|
204+
|-------|----|--------|
205+
|1.0|October 10, 2026|Initial release|
206+
207+
[!INCLUDE [DISCLAIMER](../../docfx/includes/DISCLAIMER.md)]
208+
<img src="https://m365-visitor-stats.azurewebsites.net/script-samples/scripts/exo-mailboxes-with-archive-disabled" />
58.7 KB
Loading
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
[
2+
{
3+
"name": "exo-mailboxes-with-archive-disabled",
4+
"source": "pnp",
5+
"title": "Exchange Online Mailboxes with Archive Disabled",
6+
"shortDescription": "This script identifies Exchange Online mailboxes without an enabled archive and exports the results to a timestamped CSV report.",
7+
"url": "https://pnp.github.io/script-samples/exo-mailboxes-with-archive-disabled/README.html",
8+
"longDescription": [
9+
"Connects to Exchange Online using certificate-based application authentication, retrieves mailboxes and their archive status, then reports mailboxes where ArchiveStatus is None. The read-only assessment creates a timestamped CSV report and execution log."
10+
],
11+
"creationDateTime": "2026-10-10",
12+
"updateDateTime": "2026-10-10",
13+
"products": [
14+
"Office"
15+
],
16+
"metadata": [
17+
{
18+
"key": "POWERSHELL",
19+
"value": "7.2.0"
20+
}
21+
],
22+
"categories": [
23+
"Report",
24+
"Security"
25+
],
26+
"tags": [
27+
"Connect-ExchangeOnline",
28+
"Get-EXOMailbox",
29+
"Export-Csv",
30+
"ArchiveStatus"
31+
],
32+
"thumbnails": [
33+
{
34+
"type": "image",
35+
"order": 100,
36+
"url": "https://raw.githubusercontent.com/pnp/script-samples/main/scripts/exo-mailboxes-with-archive-disabled/assets/preview.png",
37+
"alt": "Preview of the sample Exchange Online Mailboxes with Archive Disabled"
38+
}
39+
],
40+
"authors": [
41+
{
42+
"gitHubAccount": "ojopiyo",
43+
"company": "",
44+
"pictureUrl": "https://github.com/ojopiyo.png",
45+
"name": "Josiah Opiyo"
46+
}
47+
],
48+
"references": [
49+
{
50+
"name": "Exchange Online PowerShell module",
51+
"description": "Learn how to connect to Exchange Online and use Exchange Online PowerShell cmdlets.",
52+
"url": "https://learn.microsoft.com/powershell/exchange/connect-to-exchange-online-powershell"
53+
}
54+
]
55+
}
56+
]

0 commit comments

Comments
 (0)