Migrate from EWS to Microsoft Graph API

Published:8 July 2026 - 6 min. read

Audit Active Directory for stale users, weak passwords, and other security risks with Specops Password Auditor.

Microsoft 365 automation that still depends on Exchange Web Services (EWS) needs a plan. EWS has been the dependable workhorse for mailbox automation for years, but Microsoft now points Exchange Online developers and administrators toward Microsoft Graph for modern mail, calendar, user, and group access.

If you own an old PowerShell script, help desk tool, calendar integration, or background service that talks to /EWS/Exchange.asmx, this tutorial walks you through a practical migration path. You will inventory what the EWS workload does, map each operation to Microsoft Graph, set up the Microsoft Graph PowerShell SDK, and test the common mailbox and calendar patterns most teams need first.

Prerequisites

To follow along, you will need:

  • A Microsoft 365 tenant with Exchange Online.

  • A test mailbox you are allowed to query.

  • PowerShell 7 or Windows PowerShell 5.1.

  • Permission to use or request Microsoft Graph permissions in Microsoft Entra ID.

  • Basic familiarity with EWS concepts such as ExchangeService, Autodiscover, impersonation, and EWS operation names.

[!NOTE]

The examples below are safe patterns, but Graph calls that read real mailbox data require tenant authentication and consent. Run them in a test tenant or against a non-production mailbox first.

Understanding What Changes When You Leave EWS

EWS and Microsoft Graph solve similar business problems, but they do it with different shapes.

EWS is a SOAP-based API that sends XML over HTTP/S to Exchange. EWS applications commonly use the EWS Managed API, ExchangeService, Autodiscover, impersonation, and operation names such as FindItem, GetItem, CreateItem, and UpdateItem.

Microsoft Graph is a REST API. Instead of building SOAP envelopes, you call resources such as users, messages, mail folders, and events. Authentication and authorization move through Microsoft Entra ID and OAuth, using delegated or application permissions.

That shift means you should not treat the migration as a simple endpoint replacement. A better approach is to map behavior:

EWS concept Microsoft Graph concept
ExchangeService client object Graph SDK client or REST call to https://graph.microsoft.com
SOAP XML request REST request with JSON response
Autodiscover endpoint resolution Graph resource paths such as /users/{id}/messages
EWS impersonation Delegated permissions, application permissions, and access policies
FindItem and GetItem List and get message/event endpoints
EWS subscriptions Graph change notifications or delta queries

Microsoft publishes an EWS-to-Graph API mapping reference, and that page should become part of your migration workbook.

Step 1: Inventory Every EWS Dependency

Start with discovery. Before rewriting code, find every place your environment uses EWS. Search source control, scheduled task servers, automation accounts, config files, and vendor integration settings.

Look for common EWS markers:

Select-String -Path .\* -Recurse -Pattern \
    'ExchangeService','Microsoft.Exchange.WebServices','EWS/Exchange.asmx','FindItem','GetItem','AutodiscoverUrl'
[!TIP]

Include configuration files in the search, not just source code. Many legacy jobs store the EWS URL or service account in a JSON, XML, INI, or scheduled task export.

For each hit, capture the following details in a spreadsheet or issue tracker:

Field Why it matters
Application or script name Gives you an owner and migration scope
EWS operations used Drives the Graph endpoint mapping
Authentication method Determines delegated vs. application permission design
Mailboxes touched Helps limit permission scope
Business process Helps prioritize cutover order
Schedule or trigger Identifies outage windows and rollback needs
Output dependencies Shows reports, files, or downstream systems to validate

This step is not glamorous, but it prevents the common migration failure: rewriting the visible script while missing the scheduled task that runs once a month.

Step 2: Choose Delegated or Application Permissions

Graph permissions are where many EWS migrations slow down. EWS-era code often assumes a service account can reach many mailboxes. Graph makes you describe that access through permissions and admin consent.

Use delegated permissions when the automation runs as a signed-in user and should only do what that user can do. This model fits help desk tools, interactive scripts, and user-driven mailbox utilities.

Use application permissions when a background service runs without a signed-in user. This model fits daemons, nightly jobs, and integrations. Application permissions are powerful, so do not approve broad tenant-wide access unless the workload truly needs it.

For mail workloads, Microsoft explains that Graph can access Outlook mail with the appropriate delegated or application mail permissions. Your security review should answer three questions before code is written:

  1. Which mailbox data does the app need?

  2. Is the app acting as a user or as itself?

  3. Can access be limited to a mailbox group, test mailbox, or application access policy?

[!WARNING]

Do not grant Mail.ReadWrite or broad application permissions just to make the first proof of concept work. Start with read-only permissions, test the smallest scenario, and expand only when the migration requires it.

Step 3: Install and Connect the Microsoft Graph PowerShell SDK

The Microsoft Graph PowerShell SDK is a good bridge for administrators because it lets you test Graph behavior before you rewrite full applications.

Install the SDK from the PowerShell Gallery:

Install-Module Microsoft.Graph -Scope CurrentUser

Connect with the scopes you need for a basic mail read test:

Connect-MgGraph -Scopes 'User.Read','Mail.Read','Calendars.Read'

Check your context after signing in:

Get-MgContext | Select-Object Account, TenantId, Scopes

If your tenant requires admin consent for these scopes, the connection step will not complete until an administrator approves the permissions.

Step 4: Rewrite a Basic Mailbox Read

Many EWS scripts start by finding recent messages. In EWS, that usually means FindItem against a folder. In Graph PowerShell, start with Get-MgUserMessage.

$userId = '[email protected]'

Get-MgUserMessage -UserId $userId -Top 10 -Property 'id,subject,receivedDateTime,from' |
    Select-Object Subject, ReceivedDateTime, @{Name='From';Expression={$_.From.EmailAddress.Address}}

The equivalent REST shape is easy to understand:

“`plain text
GET https://graph.microsoft.com/v1.0/users/[email protected]/messages?$top=10&$select=id,subject,receivedDateTime,from

Notice two important differences from EWS:

- You request JSON properties with `$select` instead of binding to strongly typed EWS properties.

- The message ID you receive is a Graph resource ID. Do not assume it matches an EWS ID stored in an old database.

## Step 5: Rewrite Calendar Reads

Calendar integrations are another common EWS dependency. Graph exposes calendar and event resources directly.

Use the SDK to list upcoming events:

$userId = ‘[email protected]
$start = (Get-Date).ToUniversalTime().ToString(‘o’)
$end = (Get-Date).AddDays(7).ToUniversalTime().ToString(‘o’)

Get-MgUserCalendarView -UserId $userId -StartDateTime $start -EndDateTime $end |
Select-Object Subject, Start, End, Organizer

For REST-based applications, the same idea targets the calendar view endpoint:

```plain text
GET https://graph.microsoft.com/v1.0/users/[email protected]/calendarView?startDateTime=2026-07-05T00:00:00Z&endDateTime=2026-07-12T00:00:00Z

This is where you should validate time zones, recurrence behavior, and organizer fields. Calendar bugs are easy to miss if you only test one simple meeting.

Step 6: Handle Message Trace and Reporting Carefully

Not every Exchange admin task has a clean one-for-one Graph replacement. For example, message trace and some reporting workflows are often better handled through Exchange Online PowerShell cmdlets rather than Graph mail endpoints.

If your EWS-era process mixes mailbox item access with Exchange reporting, split it into two workstreams:

  • Use Microsoft Graph for mailbox, user, group, and calendar operations that Graph supports.

  • Use Exchange Online PowerShell for Exchange admin reporting and transport-related tasks that remain in the Exchange management plane.

That split keeps your migration honest. The goal is not to force every Exchange task through Graph; the goal is to remove unsupported EWS dependencies without breaking the business workflow.

Step 7: Build a Validation Checklist

Before cutover, validate the migration with the people who own the process, not just the code.

Use this checklist:

  • Confirm every EWS operation has a Graph, Exchange Online PowerShell, or documented replacement path.

  • Confirm permissions are least-privilege and approved.

  • Test with a normal mailbox, shared mailbox, and any special mailbox type the workload supports.

  • Compare old and new output for the same time window.

  • Test throttling and retry behavior.

  • Log request IDs and errors so support can troubleshoot failures.

  • Keep the old job disabled but recoverable during the first production run.

A simple comparison harness can help during parallel testing:

$userId = '[email protected]'
$outputPath = '.\graph-mail-sample.csv'

Get-MgUserMessage -UserId $userId -Top 25 -Property 'subject,receivedDateTime,from' |
    Select-Object Subject, ReceivedDateTime, @{Name='From';Expression={$_.From.EmailAddress.Address}} |
    Export-Csv -Path $outputPath -NoTypeInformation

Write-Host "Graph sample exported to $outputPath"
[!NOTE]

Keep one exported sample from the old EWS job and one exported sample from the new Graph job. Ask the business owner to review the fields, not just the developer who wrote the script.

Common Migration Pitfalls

Avoid these traps as you work through the migration:

  • Assuming EWS and Graph IDs are interchangeable. Store new Graph IDs separately and plan any lookup migration.

  • Skipping consent review. A working script with excessive permissions creates a security problem.

  • Ignoring shared mailboxes. Validate shared mailbox access early because it exposes permission design issues.

  • Testing only happy-path messages. Include attachments, meetings, recurring events, and large folders.

  • Forgetting non-code dependencies. Scheduled tasks, vendor connectors, and runbooks often hide EWS URLs in configuration files.

Wrapping Up

Migrating from EWS to Microsoft Graph API is a manageable project when you treat it as a workflow migration. Inventory the old EWS behavior, map each operation, choose the right permission model, and prove the replacement with Graph PowerShell before rewriting production code.

The biggest mindset shift is moving from a service-account SOAP model to a permissioned REST model. Once you make that shift explicit, your migration plan becomes clearer: small tests, least-privilege permissions, side-by-side validation, and a controlled cutover.

Your next step is to pick one low-risk EWS script, map its operations against Microsoft’s EWS-to-Graph reference, and produce the same output with Microsoft Graph PowerShell. Once that works, you have a repeatable pattern for the rest of your EWS estate.

Hate ads? Want to support the writer? Get many of our tutorials packaged as an ATA Guidebook.

Explore ATA Guidebooks

Looks like you're offline!