Handoff · Azure Admin

PayCargo Foundation — Power BI App Registration

Azure Entra app registration for delegated Power BI REST API access from a platform cartridge

1Purpose

We are wiring a PayCargo AI Platform cartridge to the Power BI REST API. The cartridge will call Power BI on behalf of the signed-in user (delegated access, OAuth 2.0 auth-code flow), so each employee only sees the reports they already have access to in Power BI. This mirrors the shape of the Salesforce Connected App handoff earlier this year, different vendor.

2Steps in the Microsoft Entra admin center

  1. Open portal.azure.com › Microsoft Entra ID › App registrations › New registration.
  2. Name: PayCargo AI Platform — Power BI (delegated). Supported account types: Accounts in this organizational directory only (single tenant). Redirect URI: leave blank for now (see Section 4).
  3. Click Register. On the Overview blade, copy the Application (client) ID and the Directory (tenant) ID.
  4. Open API permissions › Add a permission › Power BI Service › Delegated permissions. Tick the scopes in Section 3 and click Add permissions.
  5. At the top of the API permissions blade, click Grant admin consent for [PayCargo tenant]. All Power BI .All scopes are high-privilege delegated permissions and require tenant admin consent; individual users cannot self-consent.
  6. Open Certificates & secrets › New client secret. Description: forge-cartridge-powerbi. Expiry: 24 months. Copy the Value immediately — Azure only shows it once.
  7. Send back the four values in Section 5. Also note the secret expiry date so we can add it to the shared renewal calendar.

3Tenant setting — Fabric admin portal (confirmed required)

A live smoke test on 2026-07-08 hit this blocker: PowerBIFeatureDisabled — 'DatasetExecuteQueries' is not enabled for the tenant. The cartridge's core operation (running DAX queries via the REST API) is switched off tenant-wide. Without this toggle, the app registration works but every query returns this error.

This lives in the Fabric admin portal, not Entra — it may be a different admin than the person doing Section 2:

  1. Open app.powerbi.com › gear icon › Admin portal › Tenant settings.
  2. Search the tenant settings for Execute Queries. In current portals the setting is named Semantic Model Execute Queries REST API (Microsoft renamed datasets to semantic models; the API docs and the error string still say Dataset — same setting). It lives under Integration settings.
  3. Set to Enabled. Recommend scoping to Specific security groups rather than the entire organization — create or reuse a group for platform API users and add the pilot users (starting with mtang@paycargo.com).
  4. Changes propagate within ~15 minutes. Mike re-runs the smoke test to confirm.
If the setting shows Enabled but the API still returns the error, the gap is almost always in the group scoping: the caller must be a direct member of a security group in the Apply-to list. Three silent killers: (a) M365 groups and distribution lists do not count, security groups only; (b) membership via a nested group resolves unreliably; (c) group-membership changes reach Power BI through a cache that can lag up to 24 hours, so a same-day retest after a membership change can false-negative.

4Power BI delegated scopes

Minimum read-only set. Tick these under Power BI Service › Delegated permissions.

Delegated scopeWhat it unlocks
Report.Read.AllList and read reports in My workspace and in workspaces the user belongs to
Dataset.Read.AllRead dataset metadata, tables, and query results
Workspace.Read.AllList workspaces the user has access to
Dashboard.Read.AllRead dashboards and tiles (add only if we surface dashboards)
offline_accessRefresh tokens; required so the Lambda can refresh without re-prompting the user
openid, profile, emailStandard OIDC scopes for the sign-in
Do NOT grant Dataset.ReadWrite.All, Report.ReadWrite.All, Workspace.ReadWrite.All, or anything under Tenant.Read.All / Tenant.ReadWrite.All. Those are admin or destructive and out of scope for this cartridge.

5Redirect URI (Mike will send after cartridge deploy)

The cartridge Lambda is the OAuth client; the redirect URI is the cartridge-owned callback endpoint (not Cognito's hosted UI — that is a separate SAML flow for sign-in identity). Shape:

https://<cartridge-frontend-domain>/oauth/callback

Configure it under Authentication › Add a platform › Web. Leave Implicit grant and Hybrid flows both unchecked — we use auth code + PKCE, no implicit tokens.

6What Mike needs back

ValueWhere it lands on the Forge side
Directory (tenant) ID/forge/oauth/powerbi/tenant-id (AWS Secrets Manager)
Application (client) ID/forge/oauth/powerbi/client-id (AWS Secrets Manager)
Client secret value/forge/oauth/powerbi/client-secret (AWS Secrets Manager, encrypted with our KMS CMK)
Secret expiry dateShared renewal calendar
Security note: the client secret is a bearer credential — equivalent to a password for the app. Paste it into a 1Password shared vault or Slack DM with disappearing messages. Do not put it in email or a ticket. If it ever leaks into a log, revoke and rotate immediately.

7Consent model

8Gotchas we already know about

9Pilot target

The first cartridge use case reads one dataset on behalf of the signed-in user:

Dataset IDcfdb756f-c3c0-4113-a504-32a161012c49
Pilot usermtang@paycargo.com
Nothing dataset-specific goes into the Azure app registration. Delegated access means the cartridge sees exactly what the signed-in user sees. This table is context: the pilot user must hold Viewer (or better) on the dataset's workspace and a Power BI Pro/PPU license, or the API returns 401/404 even with a perfect app registration.

Access model confirmed 2026-07-08: the pilot dataset is shared with the pilot user at the item level (Build permission verified — Explorations works in the UI); the user is not a member of the dataset's home workspace. The cartridge therefore uses the dataset-scoped endpoint POST /v1.0/myorg/datasets/{datasetId}/executeQueries, which honors item-level shares — no workspace membership or workspace ID needed. The only remaining blocker on the API path is the tenant setting in Section 3.

10References