1. Knowledge Base
  2. Control Monitor
  3. Identity Management Integrations

How to connect M365 and Entra without granting Global Reader

This article describes the process to create an App Registration with appropriate permissions, without granting Global Reader. This will create an create an app that can be used for both M365 and Microsoft Entra integrations.

 

Note: The only method from Microsoft to grant "View only recipient as described below, is via Powershell.  

Create an App Registration

  1. Navigate to the App registrations section in the Azure Portal.
  2. Select "+ New registration" toward the top of the page.
  3. Enter a name for your application
  4. Choose Single tenant as the supported account type (Accounts in this organizational directory only). Click Register.
  5. Leave Redirect URI (optional) as it is.
  6. After registration:
    1. Note down the Application (client) ID and Directory (tenant) ID from the app's Overview page.
  7. In the left menu, expand the Manage section and select Certificates & secrets and create a new Client Secret:
    1. Click New client secret, enter a description, and set an expiration period.
    2. Note down the generated Client Secret Value (you won’t be able to view it later).

we now have:

  • APP ID (Application (client) ID) from step 6
  • TENANT ID (Directory (tenant) ID) from step 6
  • APP SECRET (Client Secret Value) from step 7

Granting API Permissions

  1. In the left menu of the app you created, select API permissions and click Add a permission
  2. Under "APIs my organization uses" search for: "Office 365 Exchange Online" select "Office 365 Exchange Online"
  3. Select Application permissions (not Delegated) and add the following API Permissions.
  • Exchange.ManageAsApp

Granting "View-only recipients" permission 

Run the following Powershell script after modifying $AppName    = "your_app" to reflect the app you created above.  After running you will be prompted to authenticate and the permission will be granted.  At this point you can connect the integration in the Prelude Platform using the App ID, Tenant ID and Secret Value you documented above.

<#
.SYNOPSIS
Assigns an Exchange Online management role to a Microsoft Entra ID (Azure AD) app (service principal).

.DESCRIPTION
- Connects to Microsoft Graph and Exchange Online.
- Finds the specified Azure AD App by display name.
- Creates a service principal if needed.
- Assigns the specified Exchange Online management role to the service principal.
#>

# CONFIGURATION
$AppName    = "app"
$RoleName   = "View-Only Recipients"
$SPName     = "SP for App $AppName"

# CLEAN OUTPUT
$ProgressPreference = 'SilentlyContinue'

# Ensure required modules are available
if (-not (Get-Command Get-MgServicePrincipal -ErrorAction SilentlyContinue)) {
    try {
        Write-Host "Installing Microsoft.Graph.Applications module..." -ForegroundColor Cyan
        Install-Module Microsoft.Graph.Applications -Scope CurrentUser -Force -AllowClobber
    } catch {
        Write-Error "Failed to install Microsoft.Graph.Applications: $_"
        exit 1
    }
}

# Connect to Graph and Exchange Online
try {
    Connect-MgGraph -Scopes "Application.Read.All", "Directory.Read.All"
    Connect-ExchangeOnline -ErrorAction Stop
} catch {
    Write-Error "Connection failed: $_"
    exit 1
}

# Find Azure AD App
$AzureADApp = Get-MgServicePrincipal -Filter "DisplayName eq '$AppName'"
if (-not $AzureADApp) {
    Write-Warning "Azure AD App '$AppName' not found. Exiting."
    exit 1
}

Write-Host "Found Azure AD App: $($AzureADApp.AppId)" -ForegroundColor Green

# Create Service Principal (if it doesn't exist in EXO)
try {
    $null = New-ServicePrincipal -AppId $AzureADApp.AppId -ObjectId $AzureADApp.Id -DisplayName $SPName -ErrorAction Stop
    Start-Sleep -Seconds 5  # Wait for propagation
} catch {
    Write-Warning "Service Principal may already exist or failed to create: $_"
}

# Retrieve SP from Exchange Online
try {
    $SP = Get-ServicePrincipal -Identity $SPName -ErrorAction Stop
} catch {
    Write-Error "Failed to find or retrieve Service Principal '$SPName'."
    exit 1
}

# Check for existing role assignment
$existingAssignment = Get-ManagementRoleAssignment -Role $RoleName -GetEffectiveUsers |
    Where-Object { $_.RoleAssigneeType -eq "ServicePrincipal" -and $_.App -eq $SP.ObjectId }

if ($existingAssignment) {
    Write-Host "Service Principal is already assigned to role '$RoleName'. Skipping assignment." -ForegroundColor Yellow
} else {
    try {
        New-ManagementRoleAssignment -Role $RoleName -App $SP.ObjectId -ErrorAction Stop
        Write-Host "✅ Role '$RoleName' assigned to Service Principal '$($SP.DisplayName)'" -ForegroundColor Green
    } catch {
        Write-Error "Failed to assign role: $_"
    }
}