Public Preview Exchange Online Admin API
Hi All,
This Week, Microsoft has annouced the Public Preview of Exchange Online Admin API.
It’s a REST based API, specifically designed to help organizations migrate away from EWS for specific Exchange admin scenarios and modernize automation workflows over HTTPS.
- Keep in Mind the Deprecation of Exchange Web Services in Exchange Online October 2026
Capabilities available in Public Preview The Admin API Public Preview includes 6 endpoints. The list of endpoints and supported functionalities are as follows:
- OrganizationConfig — Read tenant wide MailTips related configuration.
- AcceptedDomain — List accepted domains and core domain settings for the tenant.
- Mailbox — Read mailbox properties and manage Send on behalf delegates (view/update).
- MailboxFolderPermission — List, grant, modify, and remove folder level permissions (Inbox, Calendar, subfolders).
- DistributionGroupMember — Retrieve membership for distribution groups.
- DynamicDistributionGroupMember — Retrieve membership for dynamic distribution groups.
Entra Application
Go to the Microsoft Entra Admin Center and create a new Application
The Application has been created. Copy the Application ID, Object ID and Tenant ID.
Upload the Certificate *.cer File of a Certificate where you have the private Key.
Select the *.cer File and add a Description
Certificate has now been configured
Add Permission
Select “API’s my organization uses” and search for “Office 365 Exchange Online”
Decide between “Delegated” and “Application” Permission
In my case, i use an Application Permission and add the “Exchange.ManageAsAppV2” Permission
I have removed all other Permissions and “Grant admin Consent” for my Tenant
Grant Admin Consent
Now the Application has been set up
Have a look at the Manifest - it helps you get the right Permissions when aquiring the Token
Create Service Principal in Exchange Online
Part’s of that Process are also described here
Get the Entra App with Graph
###############################################################################
# Get AzureAD Application with Microsoft.Graph PowerShell
###############################################################################
Connect-MgGraph -Scopes 'Application.Read.All' -NoWelcome
$ServicePrincipalDetails = Get-MgServicePrincipal -Filter "DisplayName eq 'EXORestAPI'"
$ServicePrincipalDetails
Create a Service Principal in Exchange
###############################################################################
# Create Exchange Service Principal
###############################################################################
Connect-ExchangeOnline -ShowBanner:$false
New-ServicePrincipal -AppId $ServicePrincipalDetails.AppId -ServiceId $ServicePrincipalDetails.Id -DisplayName "EXO Serviceprincipal $($ServicePrincipalDetails.Displayname)"
Check Service Principal in Exchange
$AppID = "b7ae7b63-805d-4a78-962b-8e2e7d658901" # EXORestAPI
$ServicePrincipal = Get-ServicePrincipal | where {$_.AppId -eq "$AppID"}
$ServicePrincipal
Add App to RoleGroup
For the /OrganizationConfig and /AcceptedDomain Endpoints the Service Principal needs to have “View-Only Organization Management” RBAC Role, for the other Endpoints you need only the “Recipient Management” RBAC Role.
###############################################################################
# Add Members To RoleGroup
###############################################################################
#Get-RoleGroup -Identity "Recipient Management" | Format-List
Get-RoleGroup -Identity "View-Only Organization Management" | Format-List
Update-RoleGroupMember -Identity "View-Only Organization Management" -Members @{Add="$ServicePrincipal"}
Get Access Token
How to Authenticate is documented here
I use the PSMSALNet PowerShell Module for Authentication
Note PSMSALNet only works in PowerShell 7
Get the Access Token
###############################################################################
# PSMSALNet (Application Permission with Certificate)
###############################################################################
#Install-PSResource PSMSALNet
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$AppID = "b7ae7b63-805d-4a78-962b-8e2e7d658901" # EXORestAPI
$Certificate = Get-Item "Cert:\CurrentUser\My\A3A07A3C2C109303CCCB011B10141A020C8AFDA3" #O365Powershell4.cer
$CustomResource = "00000002-0000-0ff1-ce00-000000000000"
$HashArguments = @{
TenantId = $TenantId
ClientId = $AppID
ClientCertificate = $Certificate
Resource = 'Custom'
CustomResource = $CustomResource
}
#Get AccessToken
$Token = Get-EntraToken -ClientCredentialFlowWithCertificate @HashArguments
$AccessToken = $token.AccessToken
Let’s view the Access Token with JWTDetails PowerShell Module - it’s application only - no user involved.
###############################################################################
# View AccessToken
###############################################################################
#Install-PSResource JWTDetails
Get-JWTDetails -token $AccessToken
Exchange Online Admin API - OrganizationConfig
Let’s get the Exchange Online OrganizationConfig
###############################################################################
# Use Exchange Online Admin API - OrganizationConfig
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/OrganizationConfig"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-OrganizationConfig"
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
There are a lot of Attributes, let’s check for the PlusAddressing Setting.
#Search vor an Attribute
$Result.Value | fl *plus*
# Show Value of PlusAddressing
$Result.Value.DisablePlusAddressInRecipients
Exchange Online Admin API - AcceptedDomain
With the following Endpoint we can check the Accepted Domains.
###############################################################################
# Use Exchange Online Admin API - AcceptedDomain
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/AcceptedDomain"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-AcceptedDomain"
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value.DomainName
Here are all the Settings just for one Domain
#List all properties of a Domain
$Result.Value[1]
Exchange Online API - Mailbox
Now you need to add an X-AnchorMailbox in the Header. There are multiple ways to do that, i’ve choosen the UPN.
###############################################################################
# Use Exchange Online Admin API - Mailbox
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/Mailbox"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-Mailbox",
"Parameters": {
"ResultSize": 10
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value.DisplayName
Display Attributes of a single Mailbox
# Display Attributes of a single Mailbox
$Result.Value[3]
You can filter for attributes with a Graph style select. Note that the $ needs to be escaped
###############################################################################
# Use Exchange Online Admin API - Mailbox with Filter for Attributes
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/Mailbox?`$select=DisplayName,PrimarySmtpAddress"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-Mailbox",
"Parameters": {
"ResultSize": 10
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
Delegated Permissions
For a delegated API Permission we need the Exchange.ManageV2 Permission
Add we need to add “Mobile and desktop applications” and add a redirect uri
You also need to create the Exchange Service Principal as mentioned earlyer in this Article.
Now let’s add a user to the Exchange “Recipient Management” RBAC Role
###############################################################################
# Add Members To RoleGroup
###############################################################################
Connect-ExchangeOnline -ShowBanner:$false
Get-RoleGroup -Identity "Recipient Management" | Format-List
Update-RoleGroupMember -Identity "Recipient Management" -Members @{Add="a.bohren@icewolf.ch"}
Get Access Token with the PSMSALNet PowerShell Module
###############################################################################
# PSMSALNet (Delegated Permission)
###############################################################################
#Install-Module PSMSALNet
$HashArguments = @{
ClientId = "b7ae7b63-805d-4a78-962b-8e2e7d658901" # EXORestAPI"
TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
RedirectUri = "http://localhost" # must match Azure registration
Resource = "Custom"
CustomResource = "https://outlook.office365.com"
Permissions = @("Exchange.ManageV2") # delegated permission
}
# Prompt the user to sign in and consent, return the token
$Token = Get-EntraToken -PublicAuthorizationCodeFlow @HashArguments
$AccessToken = $token.AccessToken
Let’s have a look at the Access Token. The token contains now a user.
#View AccessToken
Get-JWTDetails -token $AccessToken
Get Exchange Online Mailboxes with Resultsize 10 and a graph style filter for Attributes that are returned by the API
###############################################################################
# Use Exchange Online Admin API - Mailbox with Filter for Attributes
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/Mailbox?`$select=DisplayName,PrimarySmtpAddress"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-Mailbox",
"Parameters": {
"ResultSize": 10
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
Let’s have a look at a Mailbox Folder Permission - note the two backslashes between Mailbox and Foldername
###############################################################################
# Use Exchange Online Admin API - MailboxFolderPermission
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/MailboxFolderPermission"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-MailboxFolderPermission",
"Parameters": {
"Identity": "a.bohren@icewolf.ch:\\Calendar",
"ResultSize": "Unlimited"
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
Get Dynamic Distribution Group Members
###############################################################################
# Use Exchange Online Admin API - DynamicDistributionGroupMember
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/DynamicDistributionGroupMember?`$select=DisplayName,PrimarySmtpAddress"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-DynamicDistributionGroupMember",
"Parameters": {
"Identity": "DDG-Demo@icewolfch.onmicrosoft.com",
"ResultSize": "Unlimited"
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
Get Distribution Group Members
###############################################################################
# Use Exchange Online Admin API - DistributionGroupMember
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/DistributionGroupMember?`$select=DisplayName,PrimarySmtpAddress"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-DistributionGroupMember",
"Parameters": {
"Identity": "info@icewolf.ch",
"ResultSize": "Unlimited"
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
Get-DistributionGroup is not an allowed command for the “DistributionGroupMember” Endpoint - just curious what error will be returned.
###############################################################################
# Use Exchange Online Admin API - Invalid Command
###############################################################################
$TenantId = "46bbad84-29f0-4e03-8d34-f6841a5071ad"
$XAnchorMailbox = "a.bohren@icewolf.ch"
$Headers = @{"Authorization" = "Bearer "+ $AccessToken;"X-AnchorMailbox" = "UPN:$XAnchorMailbox"}
$ContentType = "application/json"
$URI = "https://outlook.office365.com/adminapi/v2.0/$TenantID/DistributionGroupMember?`$select=DisplayName,PrimarySmtpAddress"
$Body = @"
{
"CmdletInput": {
"CmdletName": "Get-DistributionGroup",
"Parameters": {
"Identity": "info@icewolf.ch",
"ResultSize": "Unlimited"
}
}
}
"@
$Result = Invoke-RestMethod -URI $URI -Headers $Headers -Method "POST" -Body $Body -ContentType $ContentType
$Result.Value
Summary
I think the Admin API might not be the right name. As it’s for fixing gap’s in EWS Management to the Graph API. A lot of these Functions are not used for Administration, it’s more of a lookup.
When i was playing around i was asking myself, why so many endpoints (URL’s). And on top of that have to specify the command and parameter with a clumsy JSON. Two Endpoints, one for Organization Configuration and Accepted Domains and one for Mailbox Operations, including looking up permissions, and distribution Group Members would have worked also fine.
Anyway - it get’s the Job done and one thing more for 3rd Party or self programmed Applications that needs to be adjusted.
Hope this Article helps you getting started.
Regards
Andres Bohren





































