Public Preview Exchange Online Admin API

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.

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

Exchange Logo

PowerShell Logo