Relay Reference

Background for a relay that is already deployed. None of this is required to get the relay working — that is Relay Setup — but it is what you need when auditing the relay, debugging a scan, or answering "what exactly can this thing do to my tenant?"

Supported cloud: the relay currently supports only Microsoft's commercial cloud. Microsoft 365 GCC, GCC High, DoD, China/21Vianet, and every other sovereign or national cloud are unsupported.

What the grant script actually grants

The grant script assigns 18 Microsoft Graph app roles, 1 Exchange Online app role, and 1 Entra ID directory role (Global Reader). Every one has a call site in the scans. Re-running the script also removes what older versions requested and the scans no longer need: seven Graph roles (audited out in August 2026) and the Exchange, Teams and SharePoint Administrator roles (replaced by Global Reader, below).

The 19 app roles (the Graph rows plus Exchange.ManageAsApp) can alternatively be granted with the optional grants.bicep template described in Step 5 — the Global Reader directory role and the BAP registration cannot, and only the script removes legacy roles.

PermissionUsed for
Organization.Read.AllOrg info, branding, technical contacts, directory sync
Directory.Read.AllUsers, groups, Entra ID roles and members
User.Read.AllGuest users, sign-in activity (stale guest detection)
Application.Read.AllApp registrations with expiring credentials
Policy.Read.AllAuthorization policy, user consent policy, admin consent workflow
RoleManagement.Read.AllPrivileged role assignment counts and aggregate PIM schedules
AccessReview.Read.AllCan read every access review, reviewer, decision and setting; Calibrant exports only aggregate recurring guest and privileged-role review coverage
Domain.Read.AllDomain list for password expiry policy checks
DelegatedAdminRelationship.Read.AllDelegated admin partner relationships
ServiceHealth.Read.AllActive M365 service health incidents
AuditLog.Read.AllLast sign-in date for stale guest detection
Group.Read.AllM365 group membership, Teams ownership check
SharePointTenantSettings.Read.AllExternal sharing, anonymous links, sync restrictions
OrgSettings-Microsoft365Install.Read.AllCan read organization-wide Microsoft 365 Apps installation settings; Calibrant exports only the update channel
BackupRestore-Configuration.Read.AllCan read Microsoft 365 Backup configuration and protected-resource lists; Calibrant exports only aggregate policy state
DeviceManagementManagedDevices.Read.AllEnrolled devices, compliance state, Defender AV status
DeviceManagementConfiguration.Read.AllCompliance policies, device configuration profiles
DeviceManagementServiceConfig.Read.AllIntune tenant-level settings
Exchange.ManageAsAppRequired for Connect-ExchangeOnline -ManagedIdentity
Global Reader roleExchange cmdlets (DKIM, transport rules, connectors, spam policies) and Teams cmdlets (federation, meeting and messaging policies). Read-only — replaced the Exchange, Teams and SharePoint Administrator roles in August 2026 after every scan cmdlet was verified to run without them
Power Platform BAP API registration (optional)Not read-only. Authorizes the Managed Identity to call the Power Platform admin REST APIs — Calibrant uses it for DLP policies, environments and flow monitoring, all GETs, but the same registration also permits admin writes such as deleting an environment, and Microsoft offers no reader variant. This registration is what grants the access — no directory role is involved. Skip it and the Power Platform checks report not evaluated
Fabric API security group + admin-portal settingsAuthorizes the Managed Identity to read Fabric/Power BI tenant settings. Again the group and the portal setting grant the access, not a directory role
The Graph roles are read-only, but they are not all narrow. The 18 Graph roles are unattended, tenant-wide application permissions. Exchange.ManageAsApp enables app-only Exchange authentication; the relay's only Exchange-authorizing directory role is the read-only Global Reader role. The table describes both Microsoft's granted scope and the smaller evidence Calibrant returns. Data minimization in the compiled collector does not reduce what the Managed Identity could read if someone gained the ability to execute different code on the VM. Protect the relay as a privileged reader; the controls are documented in How the Relay VM Is Secured.

Why no administrator role remains

Calibrant only ever reads your tenant, so it should hold reader roles. As of August 2026 it holds exactly one directory role — Global Reader — and that role is read-only. All 18 Graph application roles are explicit read permissions;Exchange.ManageAsApp enables app authentication, while Global Reader limits the Exchange commands the relay is authorized to run to read-only access.

One credential is not read-only, and it is not a role. If you enabled the optional Power Platform integration, the Managed Identity holds a management-application registration whose admin API includes write operations — we measured a DELETE against an environment returning 204, so a refusal is not what you would get. Microsoft ships no read-only variant of that registration. Calibrant issues only GETs, from a catalogue compiled into the relay build, but "Calibrant only performs reads" and "Calibrant cannot perform writes" are different statements and only the first is true here. Skipping the registration is supported and costs five controls — the full trade is documented here.

Getting there took two rounds of measurement. First we verified, cmdlet by cmdlet, that a service principal holding only Global Reader runs every Exchange and Teams check the scans perform, and that SharePoint settings need no directory role at all; Exchange, Teams and SharePoint Administrator were removed from the grant script and from deployed relays.

Power Platform Administrator and Fabric Administrator were kept a while longer, on the belief that their APIs had no reader alternative. That belief was never actually tested — and when we tested it, it was wrong. Both surfaces authorize through their own registration mechanism, and the directory role contributes nothing:

  • Power Platform — the admin APIs (api.bap.microsoft.com, api.flow.microsoft.com) authorize a service principal through the management-application registration alone. An identity holding that registration and no Power Platform Administrator role reads environments, DLP policies and flow runs successfully — and, as the callout above records, can also issue admin writes against that API. Dropping the role narrowed the blast radius (no Power Platform admin centre, no Dynamics 365 administration); it did not make the surface read-only.
  • Fabric / Power BI — the admin API (api.fabric.microsoft.com/v1/admin/tenantsettings) authorizes through membership of a security group that is enabled in the Fabric admin portal under "Service principals can access read-only admin APIs". An identity in that group holding no Fabric Administrator role reads all tenant settings successfully.

The control is the group and the registration, not the role. For contrast, an identity holding only Global Reader — no registration, no group — is refused by both: 403 from Power Platform and 401 from Fabric. So the two administrator roles were granting nothing the product used, while carrying the ability to delete Power Platform environments, rewrite DLP policy and take over Fabric workspaces. They were dropped on 2026-08-03 at a cost of zero controls; re-running the grant script removes them from an existing identity.

Both integrations remain optional: skip the Power Platform registration or the Fabric portal steps and those checks still run in the full scan but report not evaluated, with no effect on other categories.

Who can reach your tenant through the relay

The relay VM holds a Managed Identity with tenant-wide read access to Microsoft 365, and — if you enabled the optional Power Platform integration — a registration that can also write to the Power Platform admin API. Anything able to run code on that VM inherits all of it. That is inherent to how Azure Managed Identities work rather than a defect, but it has a consequence worth stating plainly:

Treat the relay's resource group as a tier-0 asset. Azure permission on it converts into Microsoft 365 permission. Someone with Virtual Machine Contributor on this resource group can run az vm run-command invoke, which executes as SYSTEM on the VM and can request tokens for the Managed Identity. In effect, granting Azure contributor rights here is equivalent to granting everything the identity holds: Global Reader across your tenant, read access to the Fabric admin API, and — where the optional Power Platform registration is in place — write access to the Power Platform admin API, including deleting environments and rewriting DLP policy.

This is not a hypothetical path — it is the same mechanism the troubleshooting commands on this page use. Practical steps:

  • Put the relay in a dedicated resource group with its own role assignments. Do not grant broad subscription-level Contributor over it.
  • Keep the list of people with write access to that resource group as short as the list of people you would make a Microsoft 365 admin.
  • runCommand operations are recorded in the Azure Activity Log as control-plane events. The log captures who ran it and when, but not the script body, and retains 90 days by default. Export to Log Analytics if you need longer retention or the ability to alert on it.
  • Consider an Azure resource lock or a custom role that omits Microsoft.Compute/virtualMachines/runCommand/action for day-to-day operators.

The Managed Identity's Graph permissions are all read-only, and so is Global Reader — which since August 2026 is the only directory role the identity holds. Every write-capable directory role Calibrant once requested has been removed after measurement. The one exception is the optional Power Platform management-application registration, which is not scoped read-only — an identity holding it can call the Power Platform admin API's write operations, and Microsoft offers no reader variant. Skip it and those checks report not evaluated; if it is already in place and you want it gone, the removal command is here. Everything else this path reaches is read access, which is still worth protecting.

Pre-installed tools

PowerShell modules

  • ExchangeOnlineManagement — mailbox policies, DKIM, transport rules, connectors
  • Microsoft.Graph — users, groups, Entra ID policies, SharePoint tenant settings
  • MicrosoftTeams — Teams policies, federation, meeting settings

All modules authenticate automatically via Managed Identity before your script runs.

Environment variables

VariableRequiredDescription
CALIBRANT_API_URLYesAlways https://www.calibrant.ai
CALIBRANT_API_KEYYesYour relay API key from the Connections page
AZURE_CLIENT_IDYesManaged Identity client ID — set automatically by the Bicep template
POLL_INTERVAL_MSNoHow often to check for commands (default: 600000ms = 10 minutes)

Viewing logs

# Last 50 lines of relay log (run from your local terminal)
az vm run-command invoke \
  --name calibrant-relay-vm \
  --resource-group calibrant-relay-rg \
  --command-id RunPowerShellScript \
  --scripts "Get-Content C:\calibrant-relay\relay.log -Tail 50"

# Check service status
az vm run-command invoke \
  --name calibrant-relay-vm \
  --resource-group calibrant-relay-rg \
  --command-id RunPowerShellScript \
  --scripts "Get-Service CalibrantRelay"