Skip to main content

Integrate with Microsoft

Follow this step-by-step guide to allow SlashID to monitor and protect your Microsoft estate. A single Microsoft tenant connection covers your Entra ID directory (users, groups, service principals, managed identities, directory roles, app registrations and their credentials), your Azure resources (subscriptions, resources, RBAC role assignments), and Microsoft 365 (SharePoint sites and drives).

The directory is always synced. The Azure resources and Microsoft 365 capabilities are enabled by default and each requires its own permission grant, described below — if you skip a grant, the corresponding section of the sync is simply empty.

Before starting

Before starting, ensure you have:

  • Global Administrator or Privileged Role Administrator privileges in your Microsoft Entra tenant, to create an app registration and grant admin consent to Microsoft Graph application permissions
  • Owner (or User Access Administrator) on the Azure subscriptions you want to monitor, to create an Azure RBAC role assignment for the Azure resources capability
IP allowlisting

If your endpoint or tenant restricts traffic by source IP, allow connections from SlashID's egress IPs published at https://cdn.slashid.com/egress.json. The list is global and stable; the syncToken field changes whenever the IPs change, so you can use it to detect drift.

The file follows the JAFAR draft format (A JSON-Based Format for Publishing IP Ranges of Automated HTTP Clients), the same convention major cloud providers use to publish their IP ranges.

Step 1: Create an app registration

  1. Go to the Microsoft Entra admin center -> Identity -> Applications -> App registrations -> New registration
  2. Fill in:
    • Name: SlashID Identity Protection
    • Supported account types: Accounts in this organizational directory only
  3. Click Register
  4. On the app's Overview page, note the Application (client) ID and the Directory (tenant) ID — you will need both later.

Step 2: Create a client secret

  1. In the app registration, go to Certificates & secrets -> Client secrets -> New client secret
  2. Enter a description (e.g. SlashID sync) and pick an expiry
  3. Click Add
  4. Copy the secret Value immediately — it is only shown once.
danger

When the secret expires, syncs will start failing with authentication errors. Create a new secret and update the connection in the SlashID Console before the expiry date.

Step 3: Grant application permissions

  1. In the app registration, go to API permissions -> Add a permission -> Microsoft Graph -> Application permissions
  2. Add the following permissions:
PermissionWhat we sync
User.Read.AllUsers
Group.Read.AllGroups and group memberships
Application.Read.AllApp registrations, service principals, managed identities, credentials
RoleManagement.Read.DirectoryDirectory roles and role assignments
Directory.Read.AllOAuth2 delegated permission grants (app consent on behalf of users)
AuditLog.Read.AllSign-in activity (last sign-in timestamps; needs an Entra ID P1/P2 license)
UserAuthenticationMethod.Read.AllMFA registration posture (registered methods and has-MFA flags; needs an Entra ID P1/P2 license)
Policy.Read.AllConditional Access policies and their user/group/role scopes
IdentityRiskEvent.Read.AllIdentity Protection risk detections (needs an Entra ID P2 license)
MailboxSettings.ReadExchange inbox rules (only if you enable mailbox-rule sync — see below)
Domain.Read.AllTenant verified domains, used to flag external forwarding (inbox rules) and external sharing (file permissions)
CustomSecAttributeAssignment.Read.AllCustom security attributes assigned to users (also needs the Attribute Assignment Reader role, see below)
Sites.Read.AllSharePoint sites and drives (Microsoft 365 capability only)
  1. Click Add permissions
  2. Click Grant admin consent for <your tenant> and confirm. Every permission must show a green "Granted" status.
note

Sites.Read.All is only required for the Microsoft 365 capability, Directory.Read.All for OAuth2 permission grants, and Domain.Read.All for classifying inbox-rule forwarding and file-sharing targets as external (without it, those targets cannot be flagged as external). Skipping any of these only removes the corresponding data; the rest of the sync is unaffected.

note

Custom security attributes require more than the CustomSecAttributeAssignment.Read.All permission: the app registration's service principal must also be assigned the Attribute Assignment Reader directory role. By design, no role (not even Global Administrator) can read custom security attributes without it, so without this assignment they sync as empty.

To assign the Attribute Assignment Reader directory role:

  1. In the Microsoft Entra admin center, go to Identity -> Roles & admins
  2. Search for and open the Attribute Assignment Reader role
  3. Click Add assignments. Under Select member(s), click No member selected, search for SlashID Identity Protection (the app registration from Step 1), select it, and click Select
  4. If a Setting tab appears (your tenant uses Privileged Identity Management), open it and choose the Active assignment type with a permanent duration. A service principal cannot self-activate an Eligible assignment, so an eligible-only grant leaves custom security attributes empty
  5. Click Assign (Add on tenants without Privileged Identity Management)
note

On some PIM-enabled tenants the member picker only lists Users and Agent Identities, so SlashID Identity Protection never appears and the portal assignment cannot be completed. Assign the role through Microsoft Graph PowerShell instead:

Connect-MgGraph -TenantId "<insert tenant id>" `
-Scopes "RoleManagement.ReadWrite.Directory","Application.Read.All" `
-UseDeviceCode

# Confirm you are connected to the tenant you are onboarding
Get-MgContext

Then assign the role:

$sp   = Get-MgServicePrincipal -Filter "displayName eq 'SlashID Identity Protection'" -Top 1
$role = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq 'Attribute Assignment Reader'" -Top 1

New-MgRoleManagementDirectoryRoleAssignment `
-PrincipalId $sp.Id `
-RoleDefinitionId $role.Id `
-DirectoryScopeId "/"

Verify with:

Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$($sp.Id)'"

To also receive near real-time audit events (sign-ins, role changes, SharePoint activity), additionally grant the Office 365 Management APIs permission:

  1. API permissions -> Add a permission -> Office 365 Management APIs -> Application permissions
  2. Add ActivityFeed.Read
  3. Click Grant admin consent again

ActivityFeed.Read is required for near real-time audit-event streaming. Pull-based scheduled syncs still work without it — you only lose event streaming between syncs — but the connection health check will flag a missing ActivityFeed.Read grant as a warning.

note

Audit-event streaming also needs unified audit logging turned on for your tenant. ActivityFeed.Read only grants permission to read the feed; it does not turn the audit log on. If unified audit logging is off, SlashID cannot start the Office 365 Management Activity subscription and the attempt fails with Tenant <id> does not exist. Unified audit logging is on by default for Microsoft 365 enterprise tenants, so most tenants need no action here.

To check it and turn it on, use Exchange Online PowerShell as an admin holding the Audit Logs role (part of the Organization Management and Compliance Management role groups). Install the module once and connect (see Microsoft's Connect to Exchange Online PowerShell guide), then check and set the value:

# One-time: install the Exchange Online PowerShell module
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser

# Connect as an admin with the Audit Logs role (browser sign-in; supports MFA)
Connect-ExchangeOnline -UserPrincipalName admin@yourtenant.onmicrosoft.com

# Check (run in Exchange Online PowerShell, not Security & Compliance PowerShell,
# where the value always reads False):
Get-AdminAuditLogConfig | Format-List UnifiedAuditLogIngestionEnabled

# Enable if it reads False:
Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true

Disconnect-ExchangeOnline

Or, in the Microsoft Purview portal, go to Audit and select Start recording user and admin activity. Allow up to an hour for the change to take effect before event streaming can start.

Step 4 (optional): Assign the Azure Reader role

This step enables the Azure resources capability — Azure subscriptions, resources, and RBAC role assignments. Skip it if you only want directory and Microsoft 365 data.

Assign the Reader role to the SlashID Identity Protection app registration (from Step 1) at one of two scopes; the assignment steps are identical once you reach Access control (IAM).

Route 1 (recommended): management group scope. Covers every subscription beneath the management group, including ones created later, so it is the broadest and most future-proof option.

  1. Open the Management groups service in the Azure portal (in the portal search box, pick the Management groups entry under Services, not the Azure Management Groups enterprise application) and select the target management group
  2. Go to Access control (IAM) -> Add -> Add role assignment
  3. Under Role, select Reader
  4. Under Members, select User, group, or service principal and search for SlashID Identity Protection
  5. Click Review + assign

Assigning at this scope requires Owner or User Access Administrator on the management group itself, not just on the subscriptions below it.

Route 2: subscription scope. Covers a single subscription; repeat it for each subscription you want to monitor.

  1. Go to the Azure portal -> Subscriptions -> select the subscription to monitor
  2. Go to Access control (IAM) -> Add -> Add role assignment
  3. Under Role, select Reader
  4. Under Members, select User, group, or service principal and search for SlashID Identity Protection
  5. Click Review + assign

To also inventory the contents of your Key Vaults — the names of keys, secrets, and certificates, never their values — additionally assign the Key Vault Reader role (same steps as above, selecting "Key Vault Reader" instead of "Reader"). The plain Reader role only sees the vaults themselves; listing what is inside is a data-plane operation. Vaults without this access are skipped individually, so partial coverage works.

note

The Key Vault Reader role applies to vaults using Azure RBAC authorization. For vaults still using legacy access policies, grant the app a list access policy per vault — or skip them; they will simply be inventoried without their contents.

To also enumerate individual blobs (files) inside storage accounts as graph nodes, enable the Sync blob inventory toggle in the connection settings (Step 5). This is off by default. When enabled, the sync walks every container in every storage account under the subscription. Storage containers are always enumerated as part of the Azure resources capability — the blob inventory opt-in goes one level deeper to list each object inside them.

caution

Blob inventory is high-cardinality — a single storage account can hold millions or billions of objects. Enable it only when you need file-level inventory rather than container-level visibility.

Blob inventory requires an additional data-plane role: assign Storage Blob Data Reader on each storage account (or at the subscription scope to cover all accounts). The subscription-level Reader role from the step above grants management-plane access only and does not allow reading blob listings. Without Storage Blob Data Reader, the blob enumeration soft-fails per account — the rest of the sync is unaffected and the account's blobs are simply absent.

Step 5: Create Your Microsoft <> SlashID Integration

  1. Go to the SlashID Console -> Identity Protection -> Configuration -> Data sources.
  2. Click Add data source
  3. Select Microsoft from the list of providers in the select menu
  4. Enter your Microsoft connection details:
SlashID Console fieldDescriptionExample
Name of the connectionArbitrary name you give to this connectionMicrosoft Production
Authoritative statusDecide whether Microsoft Entra identities are the primary source of truth when reconciling identities across providersPrimary or Secondary
Directory (tenant) IDThe tenant ID from the app registration's Overview page (Step 1)00000000-0000-0000-0000-000000000000
Application (client) IDThe client ID from the app registration's Overview page (Step 1)11111111-1111-1111-1111-111111111111
Client secretThe client secret value you created in Step 2
  1. Optionally enable the deep-crawl options:
    • Sync mailbox rules — inventories each user's Exchange inbox rules and flags rules that forward or redirect mail outside your tenant (a common persistence and exfiltration technique). Requires the MailboxSettings.Read permission from Step 3. The crawl makes one request per user, so it lengthens sync on large tenants.
    • Sync file-level permissions — inventories SharePoint/OneDrive files and their sharing permissions. This is the most expensive crawl (one pass over every file); enable it when you need file-level sharing visibility rather than site/drive granularity.
    • Sync blob inventory — enumerates every blob (file) in every Azure storage account as a graph node. Requires Step 4 to be completed and the Storage Blob Data Reader role assigned on the storage accounts. See the note in Step 4 for cardinality and role requirements.
  2. Click Connect to complete the integration.

Event streaming

Once the connection is created, SlashID automatically subscribes to change streams that need no setup on your side:

  • Directory changes — Microsoft Graph change-notification subscriptions for users and groups. Change notifications aren't supported for service principals and applications; changes to those are picked up by the daily scheduled sync instead
  • Audit events — Office 365 Management Activity feeds (Audit.AzureActiveDirectory, and Audit.SharePoint when the Microsoft 365 capability is enabled), provided ActivityFeed.Read was granted in Step 3

SlashID keeps these subscriptions alive and re-creates them automatically if they lapse.

Azure resource events (optional)

Streaming Azure resource changes (resource writes/deletes, RBAC role assignments) requires an Event Grid system topic subscription in each Azure subscription you monitor, because creating it needs write access we don't have with the Reader role. For each subscription:

az eventgrid event-subscription create \
--name slashid-identity-protection \
--source-resource-id "/subscriptions/<subscription-id>" \
--endpoint "https://api.slashid.com/ip/nhi/events/v2/microsoft_tenant/azure-resource-events" \
--endpoint-type webhook \
--included-event-types \
Microsoft.Resources.ResourceWriteSuccess \
Microsoft.Resources.ResourceDeleteSuccess \
Microsoft.Resources.ResourceActionSuccess \
--delivery-attribute-mapping Authorization static "Bearer <events token>" true

Replace <events token> with the connection's events token, shown on the connection's details page in the SlashID Console. SlashID answers the Event Grid validation handshake automatically. This step is optional: without it, Azure resource changes are still picked up by every scheduled sync and the 90-day activity-log backfill — you only lose the near real-time signal.

Sign-in and Graph-activity log streaming (optional)

Real-time sign-in and Microsoft Graph activity detections — token replay, unfamiliar sign-ins, legacy-authentication usage, and Graph OAuth2 permission-grant manipulation — are driven by Entra ID's sign-in and Graph-activity logs. These logs are not part of the Graph change-notification, Management Activity, or Event Grid streams, so they flow through a separate route: an Entra ID diagnostic setting that streams the relevant log categories to an Event Hub, forwarded to SlashID by the official SlashID forwarder — a small Azure Function we build and maintain (slashid/azure-logstream-fn). It is the only supported relay; there is no DIY alternative.

Turn this on by enabling Event streaming (sign-in logs) on the connection, in the SlashID Console under the connection's settings. That toggle is the starting point for both setup paths below — automatic (SlashID provisions everything) or manual (you deploy the forwarder yourself).

If you grant the app registration's service principal the permissions below, SlashID automatically provisions everything the stream needs — no manual setup required.

What gets created automatically:

  • Resource Group (slashid-logs-{connection-id})
  • Event Hub namespace and an event hub (slashid-logs)
  • The SlashID forwarder (Azure Function)
  • An Entra ID diagnostic setting streaming SignInLogs, NonInteractiveUserSignInLogs, ServicePrincipalSignInLogs, and MicrosoftGraphActivityLogs to that Event Hub

Required permissions:

RoleScopePurpose
ContributorSubscriptionCreate the Event Hub, storage, and forwarder Function
Contributor/providers/Microsoft.aadiamCreate the Entra diagnostic setting

The /providers/Microsoft.aadiam grant cannot be assigned in the Azure portal. It must be run once, as a CLI command, by a Global Administrator with elevated access ("Access management for Azure resources") — Entra directory roles, including Global Administrator assigned to the service principal itself, are not honored at this scope; only an explicit RBAC role assignment works:

$sp = Get-MgServicePrincipal -Filter "displayName eq 'SlashID Identity Protection'" -Top 1
az role assignment create --assignee-object-id $sp.Id --assignee-principal-type ServicePrincipal --role Contributor --scope "/providers/Microsoft.aadiam"

Get-MgServicePrincipal resolves the service principal's object ID for you, so run it in a connected Microsoft Graph PowerShell session (Connect-MgGraph) with the Azure CLI signed in to the same tenant (az login). This object ID is not the Application (client) ID from Step 1, and not the app registration's own Object ID — those identify the application, not its service principal.

When assigning the subscription-level Contributor grant in the Azure portal, the Contributor role is listed under the Privileged administrator roles tab, not Job function roles.

For the subscription-level grant, you can use a granular custom role instead of Contributor:

  • Microsoft.Resources/subscriptions/resourceGroups/*
  • Microsoft.Resources/deployments/*
  • Microsoft.EventHub/namespaces/*
  • Microsoft.Storage/storageAccounts/*
  • Microsoft.Web/serverfarms/*
  • Microsoft.Web/sites/*
note

The /providers/Microsoft.aadiam scope only accepts the built-in Contributor role — custom roles are rejected there, so that grant has no granular alternative.

If permissions are missing: the connection keeps working — missing streaming grants never block the rest of the sync. The connection's health check in the SlashID Console shows exactly which grant is missing and how to fix it. Grant it whenever you're ready: provisioning picks it up and completes automatically, typically within about an hour, with nothing else to re-run.

Staying up to date: forwarder updates are applied automatically, typically within about an hour of a new SlashID forwarder release — no action needed on your side.

Manual Setup

If you'd rather not grant the permissions above, deploy the forwarder yourself from our public template:

  1. Click Deploy to Azure below, which opens our ARM template in the Azure portal. When prompted for the eventsToken parameter, use the value shown on the connection's details page in the SlashID Console.

    Deploy to Azure

    This creates an Event Hub namespace and hub, a storage account, and the forwarder Function App, wired to your events token. See the slashid/azure-logstream-fn README for the full parameter reference.

  2. In the Azure portal, go to Microsoft Entra ID -> Monitoring -> Diagnostic settings -> Add diagnostic setting. Select the categories SignInLogs, NonInteractiveUserSignInLogs, ServicePrincipalSignInLogs, and MicrosoftGraphActivityLogs, choose Stream to an event hub, and point it at the Event Hub namespace the template deployed — its name is the deployment's eventHubNamespaceName output.

Sign-in log export (SignInLogs, MicrosoftGraphActivityLogs) requires an Entra ID P1/P2 license on the tenant.

Staying up to date: the connection's health check flags an outdated forwarder when a newer version is available. Redeploying via the Deploy to Azure button always installs the latest published release — that's the update path for manual setups.

This step is optional either way: without it, the four sign-in / Graph-activity detections simply stay dark and the rest of the integration is unaffected. Whichever path you choose, the Event Hub and forwarder run inside your own Azure subscription, so their cost is yours.

Azure Storage resource-log streaming (optional)

Last-accessed edges — edges from an Entra user to individual blobs they read, wrote, or deleted — are built from Azure Storage resource logs. These logs are not exposed through Graph or Management Activity APIs; they must be streamed to an Event Hub via Azure Monitor diagnostic settings, then forwarded to SlashID by the same SlashID forwarder as the sign-in log stream above. As with Event Grid and sign-in logs, the connection's Reader role cannot create diagnostic settings, so this setup is yours to perform — auto-provisioning does not cover storage-account diagnostic settings.

For each storage account you want to track:

  1. In the Azure portal, go to the storage account -> Monitoring -> Diagnostic settings -> select blob (the blob service, shown as <storageAccountName>/blobServices/default) -> Add diagnostic setting.
  2. Enable the log categories StorageRead, StorageWrite, and StorageDelete.
  3. Under Destination details, choose Stream to an event hub and select the same Event Hub namespace and event hub used for the sign-in log stream in the previous step. If you haven't set up that Event Hub yet, create one now and use it for both diagnostic settings.
  4. The forwarder used for sign-in logs forwards records from the shared event hub to SlashID — no additional forwarder is needed, provided both diagnostic settings point at the same event hub.
note

Attribution caveat: only Entra-ID-authenticated (OAuth) data-plane operations carry a caller identity in Azure Storage resource logs. Access via SAS tokens or storage account keys produces no principal in the log record and therefore generates no last-accessed edge. Coverage is limited to Entra-authenticated data-plane access only.

This step is optional: without it, blob last-accessed edges are absent but the rest of the integration — including the pull blob inventory, if enabled — is unaffected.

Verification

After the first sync completes, SlashID will have synced:

  • Entra directory: users, groups and memberships, service principals, managed identities, directory roles and assignments, app registrations together with their client secrets and certificates
  • Entra extras (no additional permission): each user's manager, password-change recency, and devices discovered through group memberships. With AuditLog.Read.All, per-application last-sign-in edges are aggregated from the 30-day sign-in log; with CustomSecAttributeAssignment.Read.All (plus the Attribute Assignment Reader role), custom security attributes land on user records.
  • PIM role eligibility (if your tenant has an Entra ID P2 license — no extra permission needed): principals that are eligible for a directory role are tracked alongside active assignments, and anyone eligible for a privileged role (e.g. Global Administrator) is classified as highly privileged — eligible admins are one self-activation away from the role. Tenants without P2 simply skip this.
  • OAuth2 permission grants (if Directory.Read.All was granted): which applications have delegated access to act on behalf of your users — and with which scopes — whether consented per user or tenant-wide by an admin
  • Sign-in activity (if AuditLog.Read.All was granted and the tenant has an Entra ID P1/P2 license): each user's last interactive and non-interactive sign-in timestamps, used to surface stale and dormant accounts
  • Conditional Access policies (if Policy.Read.All was granted): each policy with its state, MFA requirements, risk conditions, and which users, groups, and roles it includes or excludes — the input for CA posture checks such as missing-MFA coverage and over-broad exclusions
  • Identity Protection risk detections (if IdentityRiskEvent.Read.All was granted and the tenant has an Entra ID P2 license): Microsoft's own risk verdicts — anomalous tokens, unfamiliar sign-in features, leaked credentials — linked to the affected users. Tenants without P2 simply skip this.
  • Hybrid identity links (no extra permission; requires an Active Directory connection in SlashID): users and groups synchronised from on-prem AD are deterministically linked to their AD counterparts by SID, so a hybrid human appears as one linked identity rather than two unrelated ones
  • Azure (if Step 4 was completed): subscriptions, resources, and RBAC role definitions and assignments, including the effective access they grant
  • Key Vault contents (if the Key Vault Reader role was assigned): the names of keys, secrets, and certificates in each readable vault — values are never read
  • Blob inventory (if blob inventory sync was enabled and the Storage Blob Data Reader role was assigned): every blob in every storage account as a graph node. Storage accounts without the data-plane role are skipped — the rest of the sync is unaffected.
  • Microsoft 365 (if Sites.Read.All was granted): SharePoint sites and their document libraries / drives
  • Mailbox rules (if mailbox-rule sync was enabled and MailboxSettings.Read granted): inbox rules that forward or redirect mail, with external destinations flagged against your tenant's verified domains
  • File-level permissions (if file-level permission sync was enabled): every SharePoint/OneDrive file and folder with its sharing permissions — who can read, write, or share each item, plus flags for anonymous links and shares to external recipients. Large tenants are bounded by a per-sync inventory budget; truncation is recorded on the sync.

You can verify the connection in the SlashID Console under Identity Protection -> Configuration -> Data sources, where the connection card shows counts of synced users, groups, service principals, and applications.

Troubleshooting

SymptomCauseFix
Authentication fails with 401 / invalid_clientThe client secret is wrong or has expiredCreate a new client secret (Step 2) and update the connection in the SlashID Console
Sync fails with 403 Authorization_RequestDeniedA Microsoft Graph application permission is missing, or admin consent was not grantedRe-check Step 3: all listed permissions must be present and show "Granted" after admin consent
No Azure subscriptions or resources appearThe Reader role assignment for the app's service principal is missingComplete Step 4 at the subscription or management-group scope
No SharePoint sites or drives appearSites.Read.All was not granted or not consentedAdd Sites.Read.All as a Graph application permission and grant admin consent (Step 3)
Event streaming fails to start (401 AF10001, empty permissions)The Office 365 Management APIs ActivityFeed.Read application permission is missing or admin consent wasn't grantedAdd ActivityFeed.Read (Office 365 Management APIs -> Application permissions) and grant admin consent (Step 3). Pull-based syncs are unaffected; event streaming and the historical audit backfill self-heal on the next daily reconcile (≤24h).
Event streaming does not start; logs show 400 ... Tenant <id> does not exist (Audit.DataServiceException)Unified audit logging is turned off for the tenant, so the Office 365 Management Activity subscription cannot be createdTurn on unified audit logging (see the note in Step 3): run Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled $true in Exchange Online PowerShell, or use Microsoft Purview -> Audit. Allow up to an hour, then event streaming self-heals on the next daily reconcile (≤24h).
Clicking Grant admin consent for ActivityFeed.Read fails with Could not grant admin consent. Your organization does not have a subscription (or service principal) for the following API(s): Office 365 Management APIsThe tenant has no service principal for the Office 365 Management APIs (and/or Microsoft Graph) first-party API — Azure only provisions these automatically when a relevant subscription or workload exists, so brand-new or lightly-licensed tenants can lack them, leaving consent with nothing to bind toCreate the missing service principal(s) via Microsoft Graph PowerShell, then retry Grant admin consent (Step 3); see the note below the table
Mailbox rules empty (or some users missing) despite MailboxSettings.Read grantedAn Exchange application access policy (or RBAC for Apps scope) excludes the app from some or all mailboxesAn Exchange mailbox-access scope (RBAC for Applications, or a legacy Application Access Policy) is blocking the app. Diagnose and widen it; see the note below the table.
note

"Could not grant admin consent... does not have a subscription (or service principal)". This means the tenant is missing the first-party service principal for that API — Azure only auto-creates these when a relevant subscription/workload exists, so consent has nothing to bind to. Create it with Microsoft Graph PowerShell (requires Global Administrator), then retry Grant admin consent:

Connect-MgGraph -TenantId "<insert tenant id>" -Scopes "Application.ReadWrite.All"

# Confirm you are connected to the tenant you are onboarding
Get-MgContext

# Office 365 Management APIs
New-MgServicePrincipal -AppId "c5393580-f805-4401-95e8-94b7a6ef2fc2"

# Microsoft Graph — only if the error also lists it and it is genuinely missing
New-MgServicePrincipal -AppId "00000003-0000-0000-c000-000000000000"
note

Diagnosing mailbox 403s. Granting MailboxSettings.Read lets the app reach every mailbox by default, but Exchange Online can narrow that with RBAC for Applications (current) or a legacy Application Access Policy; either can return 403 for some or all mailboxes while the Graph permission still shows "Granted".

Connect with Connect-ExchangeOnline as an Exchange administrator, then check whichever mechanism your tenant uses, with the app (client) ID from Step 1:

  • RBAC for Applications: run Test-ServicePrincipalAuthorization -Identity <app-id> -Resource <mailbox>. InScope: False means the app is scoped out of that mailbox.
  • Legacy Application Access Policy: run Test-ApplicationAccessPolicy -Identity <mailbox> -AppId <app-id>. AccessCheckResult: Denied means a policy is blocking it.

Then widen or remove the scope (the RBAC role-assignment scope, or the policy's RestrictAccess/DenyAccess group) so the app covers the mailboxes you want inventoried.