Guide
Office 365 Email from PowerShell
This guide is about one Microsoft-specific pain point: replacing Azure app registration, tenant admin consent, and Microsoft Graph PowerShell plumbing with a shorter Office 365 workflow. It shows how to authenticate, list, search, read, and send O365 mail from PowerShell without building a Graph app first.
Written by Nick Barraclough Product Manager
Reviewed by Hazik
The Microsoft Graph PowerShell problem
Microsoft retired Basic Auth for Exchange Online in October 2022 (Exchange Tech Community announcement) and announced the retirement of EWS for Exchange Online in October 2026 (Microsoft 365 Roadmap entry MC862873). The supported path is Microsoft Graph, which is great for compiled apps but heavyweight for PowerShell scripts that need to send a single email or read an inbox unattended.
Here is what it takes to send one email with the Microsoft Graph PowerShell SDK:
# Microsoft Graph API approach — 20+ lines to send one email
# Step 1: Register an app in Azure Portal (manual, ~10 minutes)
# Step 2: Get tenant admin to consent to Mail.Send permission
# Step 3: Install the Graph module
Install-Module Microsoft.Graph -Scope CurrentUser
# Step 4: Authenticate with specific scopes
Connect-MgGraph -Scopes "Mail.ReadWrite", "Mail.Send"
# Step 5: Build the message object
$message = @{
Subject = "Weekly Report"
Body = @{
ContentType = "HTML"
Content = "<h1>Report</h1><p>See attached.</p>"
}
ToRecipients = @(
@{
EmailAddress = @{
Address = "alice@company.com"
}
}
)
}
# Step 6: Send
Send-MgUserMessage -UserId "me" -BodyParameter @{ Message = $message }
# That is 6 steps and ~20 lines for one email.Nylas CLI replaces that 6-step process with a single command. No module installation, no scope negotiation, no message-object construction. The CLI handles OAuth token acquisition and refresh automatically after the initial nylas auth login.
# Nylas CLI approach — 1 line
nylas email send `
--to "alice@company.com" `
--subject "Weekly Report" `
--body-html "<h1>Report</h1><p>See attached.</p>" `
--yesAuthenticate your Office 365 account
Nylas CLI authenticates against the Microsoft identity platform using the same OAuth 2.0 flow that Graph apps use, but without requiring an Azure app registration or tenant admin consent. The entire setup takes under 60 seconds: install the CLI, run one login command, and the CLI stores tokens locally under ~/.config/nylas/. Compare that to the 10-15 minutes Microsoft estimates for registering a Graph application in Azure Portal.
The install command below downloads the latest release, verifies the SHA-256 checksum, and places the binary in ~/.config/nylas/bin. After install, nylas auth login opens a browser window for Microsoft OAuth consent.
# Install on Windows (PowerShell one-liner)
irm https://cli.nylas.com/install.ps1 | iex
# Authenticate — opens a browser for Microsoft OAuth
nylas auth login
# Verify your account is connected
nylas auth whoamiFor Homebrew, shell-script, and Go install paths, see the getting started guide.
List Office 365 email
Listing email from an Office 365 mailbox with Nylas CLI requires a single command instead of the 4-line Connect-MgGraph / Get-MgUserMessage sequence that Graph demands. The CLI returns messages sorted newest-first by default, with each result including subject, sender, date, and read status. The --limit flag caps results per request at up to 200 messages.
The commented-out lines show the equivalent Graph PowerShell SDK call for comparison. The CLI version reduces the request from 4 lines to 1.
# Graph API: List messages
# Connect-MgGraph -Scopes "Mail.Read"
# Get-MgUserMessage -UserId "me" -Top 10 |
# Select-Object Subject, From, ReceivedDateTime
# Nylas CLI: List messages
nylas email list --limit 10Adding --json returns structured JSON that PowerShell can pipe directly into ConvertFrom-Json. This makes it possible to filter, sort, and format email data using standard PowerShell object pipeline patterns without parsing text output.
# List emails as PowerShell objects
$emails = nylas email list --json --limit 20 | ConvertFrom-Json
# Display subject and sender
$emails | ForEach-Object {
Write-Host "$($_.from[0].name): $($_.subject)"
}
# Filter to unread
$unread = nylas email list --json --unread --limit 50 | ConvertFrom-Json
Write-Host "You have $($unread.Count) unread messages"Search Office 365 email
Searching Office 365 email through Graph requires choosing between the $filter OData parameter (limited to exact field matches) and the Search API (KQL syntax, separate permissions, different endpoint). Nylas CLI collapses both into one nylas email search command that accepts free-text queries and field-specific prefixes like from:. Results come back as the same JSON shape used by nylas email list, so the same PowerShell pipeline works for both.
The examples below show three common search patterns. Each returns up to the number of results specified by --limit, sorted by relevance.
# Graph API: Search messages
# Get-MgUserMessage -UserId "me" -Filter "contains(subject, 'quarterly report')"
# # Or use the Search API with KQL syntax...
# # which requires different permissions and a different endpoint
# Nylas CLI: Search messages
nylas email search "quarterly report" --json --limit 20 | ConvertFrom-Json
# Search by sender
nylas email search "from:cfo@company.com" --json --limit 10 | ConvertFrom-Json
# Search with date range
nylas email search "budget 2026" --json --limit 30 | ConvertFrom-JsonRead a specific email
Reading the full body of an Office 365 email requires the message ID, which Nylas CLI returns in the id field of every list and search result. The nylas email read command fetches the complete message including HTML body, all recipients, and attachment metadata in a single API call. Graph requires a separate Get-MgUserMessage call with -Select to control which properties come back, since the default response omits the body.
The second example below chains two commands: it lists the most recent message, extracts its ID, then reads the full content. This 3-line pattern replaces about 6 lines of Graph SDK code.
# Graph API: Read a message
# Get-MgUserMessage -UserId "me" -MessageId $msgId |
# Select-Object Subject, Body, From, ToRecipients
# Nylas CLI: Read a message
nylas email read msg_abc123def --json | ConvertFrom-Json
# Get the message ID from a list, then read it
$latest = nylas email list --json --limit 1 | ConvertFrom-Json
$full = nylas email read $latest[0].id --json | ConvertFrom-Json
Write-Host "Subject: $($full.subject)"
Write-Host "Body: $($full.body)"
Write-Host "From: $($full.from[0].email)"Send email from Office 365
Sending email from an Office 365 account with Nylas CLI takes one command instead of the 20-line Send-MgUserMessage sequence shown earlier. The CLI supports plain text via --body, HTML via --body-html, and multiple recipients as a comma-separated list. The --yes flag skips the interactive confirmation prompt, which is required for unattended scripts and CI/CD pipelines.
The examples below cover three sending patterns: plain text, HTML, and multi-recipient. All three use the Office 365 account authenticated in the earlier step.
# Simple text email
nylas email send `
--to "alice@company.com" `
--subject "Meeting notes" `
--body "Here are the notes from today's standup." `
--yes
# HTML email
nylas email send `
--to "team@company.com" `
--subject "Sprint Summary" `
--body-html "<h2>Sprint 14</h2><p>Completed 23 story points.</p>" `
--yes
# Multiple recipients
nylas email send `
--to "alice@company.com,bob@company.com" `
--subject "Action items" `
--body "Please review the attached proposal by Friday." `
--yesManage email folders
Office 365 organizes email into folders like Inbox, Sent Items, and Drafts. Nylas CLI maps these to the --folder flag on nylas email list, so filtering by folder does not require a separate Graph folder-ID lookup. Microsoft Graph needs the folder ID (a 120+ character opaque string) before you can list its contents; the CLI accepts human-readable names like inbox, sent, and drafts.
The --has-attachment flag filters to messages with at least one attachment. Combine it with --folder and --unread to build targeted queries without writing OData filter expressions.
# List emails in a specific folder
nylas email list --json --folder "inbox" --limit 20 | ConvertFrom-Json
nylas email list --json --folder "sent" --limit 20 | ConvertFrom-Json
nylas email list --json --folder "drafts" --limit 10 | ConvertFrom-Json
# List only emails with attachments
nylas email list --json --has-attachment --limit 20 | ConvertFrom-Json
# Combine filters
$inboxUnread = nylas email list --json `
--folder "inbox" `
--unread `
--limit 50 | ConvertFrom-Json
Write-Host "$($inboxUnread.Count) unread messages in inbox"When to keep using Graph PowerShell
Nylas CLI is a better fit for PowerShell scripts that read, search, and send mail: shorter commands, JSON output, no per-tenant app registration, and automatic token refresh. It is not a replacement for the full Microsoft Graph surface, which exposes over 15,000 API endpoints across Teams, SharePoint, Intune, and Entra ID. Keep using Microsoft.Graph when:
- You need application-only permissions. Graph supports app-only auth (
.defaultscope, client credentials flow) for service accounts that act on every mailbox in a tenant. The CLI is built around delegated user auth — one grant per mailbox. - Your script touches non-mail Graph endpoints. Teams, SharePoint, Intune, Entra ID admin operations live in Graph; the CLI only covers email, calendar, contacts, and webhooks.
- You are writing automation that must survive without external connectivity to Nylas. The CLI proxies through Nylas; Graph PowerShell talks directly to
graph.microsoft.com. If your tenant has compliance rules against third-party hops, that matters.
For everything else (daily scripts that read, search, and send mail from one or a handful of mailboxes), the CLI saves the Azure setup work. Microsoft Learn covers the Graph auth options if you need to evaluate either path.
Scripting workflows
Nylas CLI outputs JSON by default when --json is passed, which means PowerShell's ConvertFrom-Json turns every CLI response into native PSObjects. This lets you chain email operations with standard PowerShell cmdlets like Group-Object, Where-Object, and ForEach-Object without parsing text. The script below processes up to 100 unread messages and categorizes them by subject-line keywords in under 2 seconds on a typical connection.
# o365-inbox-processor.ps1
# Process unread emails and take action based on sender/subject
$unread = nylas email list --json --unread --limit 100 | ConvertFrom-Json
foreach ($email in $unread) {
$sender = $email.from[0].email
$subject = $email.subject
# Auto-categorize
switch -Regex ($subject) {
"invoice|receipt|payment" {
Write-Host "[FINANCE] $sender — $subject"
}
"deploy|release|hotfix" {
Write-Host "[DEVOPS] $sender — $subject"
}
"meeting|calendar|invite" {
Write-Host "[MEETING] $sender — $subject"
}
default {
Write-Host "[OTHER] $sender — $subject"
}
}
}
# Summary
$byCategory = $unread |
Group-Object -Property {
switch -Regex ($_.subject) {
"invoice|receipt|payment" { "Finance" }
"deploy|release|hotfix" { "DevOps" }
"meeting|calendar|invite" { "Meeting" }
default { "Other" }
}
}
Write-Host "`nSummary:"
$byCategory | ForEach-Object {
Write-Host " $($_.Name): $($_.Count) emails"
}Multiple accounts
Nylas CLI supports an unlimited number of authenticated accounts across providers. Each nylas auth login creates a separate grant, and the --grant flag on any email command targets a specific account by email address. This is useful for IT teams that manage 5-10 shared mailboxes or developers who test against both Office 365 and Gmail without switching tools. Graph PowerShell requires a separate Connect-MgGraph call (and a separate Azure app registration) per tenant.
The example below authenticates two accounts (one Office 365, one Gmail) and shows how to list and send from each. The --grant flag accepts the full email address of the target account.
# Authenticate work and personal accounts
nylas auth login # Work O365 account
nylas auth login # Personal Gmail account
# Verify current account
nylas auth whoami
# Use --grant to target a specific account
nylas email list --json --grant work@company.com --limit 10 | ConvertFrom-Json
nylas email list --json --grant personal@gmail.com --limit 10 | ConvertFrom-Json
# Send from a specific account
nylas email send `
--grant work@company.com `
--to "client@example.com" `
--subject "Proposal" `
--body "Please find the proposal attached." `
--yesFrequently asked questions
These questions come from teams evaluating Nylas CLI for Office 365 environments with enterprise security requirements. They cover conditional access, shared mailboxes, government cloud tenants, data-loss prevention, and cross-provider scripting -- the 5 topics that block adoption most often.
Will my tenant's conditional access policies block Nylas CLI auth?
Conditional access blocks the Microsoft sign-in step, not the CLI itself. If your tenant requires device compliance, MFA, or named-location restrictions, those checks fire when nylas auth login opens the Microsoft consent page. The most common failure is a tenant that disables third-party OAuth apps under Microsoft Entra ID -> Enterprise applications -> Consent and permissions; in that case ask your admin to allow the Nylas application explicitly. The same policy gate applies to any Graph SDK app.
Can I use Nylas CLI for shared mailboxes and group mailboxes?
Yes for shared mailboxes, with a caveat. Connect each shared mailbox as a separate grant in the Nylas dashboard, then target it from PowerShell with --grant. The signed-in user must already have Read or Send-As permission on the shared mailbox in Exchange Online. Microsoft 365 Group mailboxes are not currently first-class — read access works through the underlying Exchange folder, but Group-specific features (likes, mentions) do not have CLI commands.
Does it work in Microsoft 365 Government (GCC, GCC High, DoD)?
GCC and GCC High tenants use separate Microsoft identity endpoints and a different Graph base URL. Nylas's production environments target the commercial cloud, so check directly with Nylas before pointing the CLI at a GCC mailbox in production. The Office 365 US Government service description documents the endpoint and tenancy differences.
Will retention or DLP policies prevent the CLI from deleting or sending mail?
Yes. Microsoft Purview retention applies at the mailbox level — the CLI command can return success while the message is preserved by the retention policy and reappears on the next sync. Outbound DLP rules in Exchange Online and Defender for Office 365 also still apply: a message that violates a DLP policy will be quarantined regardless of which client sent it.
Can I write one script that runs for both Office 365 and Gmail mailboxes?
Yes. nylas email list, nylas email search, nylas email send, and the JSON shape of their output are identical across providers. Connect each mailbox as its own grant and switch with --grant. That is the main reason teams pair the CLI with PowerShell rather than calling Graph directly: the same script works for an Outlook mailbox, a Gmail mailbox, and an Exchange on-prem mailbox without rewrites.
Next steps
- Read and search email in PowerShell -- list unread, search by sender, export to CSV
- Download email attachments in PowerShell -- filter by type, batch download, rename
- Email and calendar automation in PowerShell -- availability checks, event creation, reminders
- PowerShell email monitoring and alerts -- disk space, service health, uptime notifications
- PowerShell email in CI/CD pipelines -- build notifications, deploy alerts, test results
- Automated email reports with PowerShell -- CSV reports, scheduled sends, templates
- Replace Send-MgUserMessage -- migrating from Graph PowerShell cmdlets
- List Outlook emails from the command line -- the bash version of O365 email access
- Send email from the terminal -- cross-platform email sending guide
- Full command reference -- every flag and subcommand