Guide
Replace Send-MgUserMessage
Microsoft replaced the deprecated Send-MailMessage with Graph PowerShell cmdlets, but the replacement is more complex, not simpler. This guide shows side-by-side before/after examples so you can drop the Azure AD ceremony and send email in one command. Works with Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP.
Written by Prem Keshari Senior SRE
Reviewed by Caleb Geene
The problem with Send-MgUserMessage
Send-MgUserMessage is Microsoft's Graph PowerShell replacement for the deprecated Send-MailMessage, but it trades a one-liner for a 6-step ceremony involving Azure AD app registration, Graph API permissions, admin consent, MSAL token management, and a two-step create-then-send workflow that takes roughly 20 lines of PowerShell to send a single email.
The cmdlet can't send a new email directly -- it only sends an existing draft. You first create the draft with New-MgUserMessage, then dispatch it in a second call. According to Microsoft's official documentation, the full workflow requires:
- Register an Azure AD app in portal.azure.com with the correct redirect URIs
- Grant Mail.Send permission (delegated or application scope) and get admin consent
- Authenticate every session with
Connect-MgGraph -Scopes "Mail.Send" - Two-step send: create a draft with
New-MgUserMessage, then fire it withSend-MgUserMessage - Token management via MSAL -- handle token refresh, expiry, and caching yourself
- Nested hashtables for message body, recipients, and attachments
That's 6 prerequisites and roughly 20 lines of PowerShell just to send one email. The Microsoft.Graph.Mail module has over 50 cmdlets, yet none of them offer a single-command send.
One-time setup
Nylas CLI replaces the entire Azure AD registration, Graph API permission grants, and MSAL token management with a single install command and one authentication step. The setup takes under 2 minutes compared to the 15-30 minutes typical for configuring a Graph PowerShell app registration with the correct redirect URIs and admin consent.
Install the CLI with irm https://cli.nylas.com/install.ps1 | iex on Windows or brew install nylas/nylas-cli/nylas on macOS/Linux (all install methods). Then authenticate once -- no Azure AD apps, no Graph permissions, no MSAL tokens.
# Authenticate (one-time)
nylas auth config
# Paste your API key from dashboard-v3.nylas.com
# Verify
nylas auth whoami
# => Authenticated as you@company.com (Microsoft 365)That's it. No app registration, no permission grants, no Connect-MgGraph. The CLI stores credentials locally and handles token refresh automatically.
Pattern 1: Basic email send
A basic email send with Send-MgUserMessage requires 15 lines of PowerShell: connecting to Graph, building a nested hashtable for the message body and recipients, creating a draft with New-MgUserMessage, then dispatching it. Nylas CLI does the same thing in 4 lines with a single nylas email send command.
Before (Graph PowerShell)
The Graph PowerShell SDK requires a Connect-MgGraph call with the Mail.Send scope at the start of every session. The message body must be structured as a nested hashtable with ContentType, Content, and ToRecipients keys. Microsoft's Graph API then requires a two-step process: first create a draft, then send it.
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Mail.Send"
# Build the message body as a nested hashtable
$message = @{
Subject = "Quarterly report"
Body = @{
ContentType = "Text"
Content = "Please review the Q4 numbers."
}
ToRecipients = @(
@{ EmailAddress = @{ Address = "colleague@company.com" } }
)
}
# Create the draft, then send it
$draft = New-MgUserMessage -UserId "you@company.com" -BodyParameter $message
Send-MgUserMessage -UserId "you@company.com" -MessageId $draft.IdAfter (Nylas CLI)
Nylas CLI sends a plain-text email with a single command. The --yes flag skips the confirmation prompt, making the command scriptable. No hashtables, no draft creation step, and no -UserId parameter -- the CLI knows which account is authenticated.
nylas email send `
--to "colleague@company.com" `
--subject "Quarterly report" `
--body "Please review the Q4 numbers." `
--yesFour lines instead of fifteen. The Graph version requires 3 separate API concepts (connection, message creation, message dispatch) where Nylas CLI uses one.
Pattern 2: Send with attachment
Sending an attachment with Send-MgUserMessage adds 8 extra lines on top of the basic send: reading the file bytes, base64-encoding them, and annotating the attachment with the correct @odata.type and MIME ContentType. The Graph API caps inline attachments at 3 MB and requires a resumable upload session for anything larger. Nylas CLI handles file reading, encoding, and MIME detection with a single --attach flag.
Before (Graph PowerShell)
Graph PowerShell requires manual base64 encoding via [System.Convert]::ToBase64String and an @odata.type annotation of #microsoft.graph.fileAttachment for each attachment. The attachment block must also specify the file's MIME ContentType -- getting it wrong can cause email clients to display the file incorrectly.
Connect-MgGraph -Scopes "Mail.Send"
# Read and base64-encode the file
$fileBytes = [System.IO.File]::ReadAllBytes("C:\Reports\invoice-march.pdf")
$base64 = [System.Convert]::ToBase64String($fileBytes)
$message = @{
Subject = "Invoice attached"
Body = @{
ContentType = "Text"
Content = "Please find the March invoice."
}
ToRecipients = @(
@{ EmailAddress = @{ Address = "client@example.com" } }
)
Attachments = @(
@{
"@odata.type" = "#microsoft.graph.fileAttachment"
Name = "invoice-march.pdf"
ContentType = "application/pdf"
ContentBytes = $base64
}
)
}
$draft = New-MgUserMessage -UserId "you@company.com" -BodyParameter $message
Send-MgUserMessage -UserId "you@company.com" -MessageId $draft.IdAfter (Nylas CLI)
Nylas CLI reads the file, detects the MIME type, and handles base64 encoding automatically. The --attach flag accepts a file path -- no manual encoding or OData type annotations needed.
nylas email send `
--to "client@example.com" `
--subject "Invoice attached" `
--body "Please find the March invoice." `
--attach "C:\Reports\invoice-march.pdf" `
--yesFive lines instead of twenty-five. The Graph version requires 4 separate .NET method calls just to prepare the attachment.
Pattern 3: HTML email
HTML emails are common for reports, newsletters, and formatted notifications -- over 70% of business email is sent as HTML according to Litmus's 2024 State of Email report. Graph PowerShell requires you to explicitly set ContentType = "HTML" in the message hashtable. Nylas CLI auto-detects HTML content in the --body argument and sets the MIME type automatically.
Before (Graph PowerShell)
In Graph PowerShell, you must explicitly set the Body.ContentType to "HTML" in the message hashtable. Omitting this field or setting it to "Text" causes HTML tags to render as literal text in the recipient's inbox. The rest of the workflow is identical to the basic send pattern -- create a draft, then dispatch it.
Connect-MgGraph -Scopes "Mail.Send"
$htmlBody = @"
<h1>Weekly Report</h1>
<p>Here are this week's metrics:</p>
<ul>
<li>Deployments: 12</li>
<li>Incidents: 0</li>
</ul>
"@
$message = @{
Subject = "Weekly report"
Body = @{
ContentType = "HTML"
Content = $htmlBody
}
ToRecipients = @(
@{ EmailAddress = @{ Address = "team@company.com" } }
)
}
$draft = New-MgUserMessage -UserId "you@company.com" -BodyParameter $message
Send-MgUserMessage -UserId "you@company.com" -MessageId $draft.IdAfter (Nylas CLI)
Nylas CLI inspects the --body content and detects HTML tags automatically. There is no ContentType field to set -- the CLI handles the MIME type behind the scenes, eliminating a common source of formatting bugs.
$htmlBody = @"
<h1>Weekly Report</h1>
<p>Here are this week's metrics:</p>
<ul>
<li>Deployments: 12</li>
<li>Incidents: 0</li>
</ul>
"@
nylas email send `
--to "team@company.com" `
--subject "Weekly report" `
--body $htmlBody `
--yesSame HTML content, fewer moving parts. The Graph version needs 22 lines; the Nylas CLI version needs 12.
Pattern 4: Send on behalf of / shared mailbox
Shared mailboxes are used by over 60% of Microsoft 365 organizations for team inboxes like support@ or sales@, according to Microsoft's own adoption data. Graph PowerShell requires a separate Mail.Send.Shared permission scope and admin consent to send from a shared mailbox. Nylas CLI handles shared mailbox access through grants -- connect the shared mailbox once, then reference it by grant ID in any command.
Before (Graph PowerShell)
Sending from a shared mailbox in Graph PowerShell requires the Mail.Send.Shared delegated permission, which is distinct from the standard Mail.Send scope. An Azure AD admin must explicitly consent to this additional permission. You then pass the shared mailbox address as the -UserId parameter to both New-MgUserMessage and Send-MgUserMessage.
# Requires Mail.Send.Shared permission in Azure AD
Connect-MgGraph -Scopes "Mail.Send.Shared"
$message = @{
Subject = "Team update"
Body = @{
ContentType = "Text"
Content = "This week's progress summary."
}
ToRecipients = @(
@{ EmailAddress = @{ Address = "stakeholders@company.com" } }
)
}
# Send as the shared mailbox by specifying its UserId
$draft = New-MgUserMessage -UserId "shared-inbox@company.com" -BodyParameter $message
Send-MgUserMessage -UserId "shared-inbox@company.com" -MessageId $draft.IdAfter (Nylas CLI)
Nylas CLI treats shared mailboxes as separate grants. Connect the shared mailbox once with nylas auth config, then pass its grant ID to any email command. No additional permission scopes or admin consent required.
# Use a different grant connected to the shared mailbox
nylas email send `
--to "stakeholders@company.com" `
--subject "Team update" `
--body "This week's progress summary." `
--grant-id <shared-mailbox-grant-id> `
--yesThe same --grant-id flag works for any Nylas CLI command -- reading, searching, or sending from the shared mailbox all follow the same pattern.
Side-by-side comparison
The table compares Graph PowerShell and Nylas CLI across 10 dimensions that matter most when migrating email automation scripts. Graph PowerShell locks you into Microsoft-only infrastructure -- Azure AD apps, MSAL tokens, and Graph API scopes -- while the CLI works across 6 providers with a single authentication model. The differences are most pronounced in setup complexity and cross-provider support.
| Feature | Graph PowerShell | Nylas CLI |
|---|---|---|
| Authentication | Azure AD app + Connect-MgGraph + MSAL | One-time nylas auth config |
| Setup steps | Register app, grant permissions, admin consent | Install + authenticate |
| Send workflow | Two-step: New-MgUserMessage then Send-MgUserMessage | Single command: nylas email send |
| Attachments | Base64-encode, annotate @odata.type | --attach file.pdf |
| HTML body | Set ContentType = "HTML" in hashtable | Auto-detected |
| Shared mailbox | Mail.Send.Shared permission + -UserId | --grant-id |
| Email scheduling | Not supported natively | --schedule |
| Read inbox | Get-MgUserMessage (separate cmdlet) | nylas email list |
| Cross-provider | Microsoft only | Gmail, Outlook, Exchange, Yahoo, iCloud, IMAP |
| Token management | Manual (MSAL refresh, caching) | Automatic |
Frequently asked questions
These are the most common questions developers ask when migrating from Send-MgUserMessage and the Graph PowerShell SDK. Microsoft's deprecation of Send-MailMessage in PowerShell 7.x pushed thousands of scripts toward Graph PowerShell, and many teams are now evaluating simpler alternatives.
What is Send-MgUserMessage?
Send-MgUserMessage is a Microsoft Graph PowerShell cmdlet that sends an existing draft message. It is part of the Microsoft.Graph.Mail module, which contains over 50 cmdlets for interacting with Microsoft 365 mailboxes. The cmdlet replaced the deprecated Send-MailMessage and requires Azure AD app registration with Mail.Send permission plus a two-step workflow: create a draft with New-MgUserMessage, then send it.
Why does Send-MgUserMessage need Azure AD app registration?
Send-MgUserMessage calls the Microsoft Graph API, which enforces OAuth2 authorization on every request. Graph API access requires an Azure AD app registration with the correct permissions configured. You register the app in portal.azure.com, configure Mail.Send as a delegated or application permission, and get admin consent before the cmdlet can send any email. Microsoft requires this even for single-user scripts.
Can I use Nylas CLI with Microsoft 365?
Yes. Nylas CLI works with Microsoft 365, Exchange Online, and 5 other major providers (Gmail, Yahoo, iCloud, Exchange on-premises, and generic IMAP). The CLI handles OAuth2 token management and Graph API authentication automatically, so you don't need to register Azure AD apps or manage permissions yourself. Authentication takes under 2 minutes.
Does Nylas CLI work with Graph API permissions?
Nylas CLI abstracts away Graph API permissions entirely. Instead of configuring Mail.Send, Mail.ReadWrite, or other Microsoft Graph scopes, you authenticate once with nylas auth config. The CLI manages provider-specific authorization for Gmail, Outlook, Exchange, Yahoo, iCloud, and IMAP through a single unified credential.
Next steps
- Replace Send-MailMessage -- migrating from the older deprecated cmdlet instead?
- Manage Office 365 email from PowerShell -- read, search, and organize your Microsoft 365 inbox
- Send email from PowerShell -- advanced patterns, scheduling, and scripting
- 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
- Full command reference -- every flag and subcommand documented
- Microsoft Graph: user: sendMail — the underlying API Send-MgUserMessage wraps, useful for diffing what the cmdlet does or doesn't expose
- Microsoft Graph PowerShell SDK: migration guidance — the official deprecation timeline that motivates moving off Send-MgUserMessage scripts