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.
| Permission | Used for |
|---|---|
Organization.Read.All | Org info, branding, technical contacts, directory sync |
Directory.Read.All | Users, groups, Entra ID roles and members |
User.Read.All | Guest users, sign-in activity (stale guest detection) |
Application.Read.All | App registrations with expiring credentials |
Policy.Read.All | Authorization policy, user consent policy, admin consent workflow |
RoleManagement.Read.All | Privileged role assignment counts and aggregate PIM schedules |
AccessReview.Read.All | Can read every access review, reviewer, decision and setting; Calibrant exports only aggregate recurring guest and privileged-role review coverage |
Domain.Read.All | Domain list for password expiry policy checks |
DelegatedAdminRelationship.Read.All | Delegated admin partner relationships |
ServiceHealth.Read.All | Active M365 service health incidents |
AuditLog.Read.All | Last sign-in date for stale guest detection |
Group.Read.All | M365 group membership, Teams ownership check |
SharePointTenantSettings.Read.All | External sharing, anonymous links, sync restrictions |
OrgSettings-Microsoft365Install.Read.All | Can read organization-wide Microsoft 365 Apps installation settings; Calibrant exports only the update channel |
BackupRestore-Configuration.Read.All | Can read Microsoft 365 Backup configuration and protected-resource lists; Calibrant exports only aggregate policy state |
DeviceManagementManagedDevices.Read.All | Enrolled devices, compliance state, Defender AV status |
DeviceManagementConfiguration.Read.All | Compliance policies, device configuration profiles |
DeviceManagementServiceConfig.Read.All | Intune tenant-level settings |
Exchange.ManageAsApp | Required for Connect-ExchangeOnline -ManagedIdentity |
| Global Reader role | Exchange 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 settings | Authorizes the Managed Identity to read Fabric/Power BI tenant settings. Again the group and the portal setting grant the access, not a directory role |
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.
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:
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.
runCommandoperations 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/actionfor 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
| Variable | Required | Description |
|---|---|---|
CALIBRANT_API_URL | Yes | Always https://www.calibrant.ai |
CALIBRANT_API_KEY | Yes | Your relay API key from the Connections page |
AZURE_CLIENT_ID | Yes | Managed Identity client ID — set automatically by the Bicep template |
POLL_INTERVAL_MS | No | How 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"