Connect Power BI semantic models to Bag of words and query them with DAX
Bag of words connects to the Power BI REST API to auto-discover the workspaces, semantic models (datasets), tables, and reports an identity can access, and to run DAX queries against them. This page covers everything you set up on the Microsoft side (app registration, tenant settings, workspace membership, permissions) and how to enter it in Bag of words.
The Power BI connector is part of the Enterprise Edition. Per-user (delegated) authentication in particular requires an Enterprise license.
Bag of words authenticates to Power BI as an Azure AD (Microsoft Entra ID) application, then:
Lists the workspaces the identity belongs to (GET /v1.0/myorg/groups).
Lists the semantic models (datasets) and reports in each workspace.
Reads each model’s tables and columns so the AI agent knows the schema. Discovery uses the read-only Admin (Scanner) API when available, and falls back to a DAX COLUMNSTATISTICS() probe per model otherwise.
Runs DAX queries at request time (POST .../executeQueries).
Each Power BI table is exposed to the agent as a schema table named Dataset/Table (for example Sales Model/Customers).
Power BI supports two authentication modes. You can use either, or both.
Service Principal (Azure AD)
A single shared application identity (a system credential) discovers and queries Power BI for everyone. Simplest to operate. Best when all users may see the same models.
Sign in with Microsoft (per-user)
Each user signs in with their own Microsoft account (OAuth / on-behalf-of). Discovery and queries run with that user’s own permissions, so each person sees only what they are allowed to. Requires Enterprise — see Authentication.
With per-user authentication, a semantic model is selectable for a user as long as their own account can see it — even if the shared service principal cannot. Usage and instructions are still tracked at the organization level per model.
In the Azure Portal → Microsoft Entra ID → App registrations → New registration.
1
Create the app registration
Give it a name (e.g. Bag of words - Power BI). Under Supported account types, choose Accounts in this organizational directory only unless you specifically need multi-tenant.
2
Copy the Tenant ID and Client ID
From the app’s Overview page, copy the Directory (tenant) ID and the Application (client) ID. You will paste these into Bag of words.
3
Create a client secret
Go to Certificates & secrets → New client secret. Copy the secret value immediately (it is only shown once). This is the client_secret field in Bag of words.
4
Add API permissions
Under API permissions → Add a permission → Power BI Service, add the delegated Tenant.Read.All (or the read scopes your policy allows). For per-user sign-in, keep the delegated profile, email, and openid scopes. Grant admin consent for the directory.
5
(Per-user only) Add the redirect URI
For Sign in with Microsoft, add a Web redirect URI under Authentication:
These live in the Power BI / Fabric Admin portal → Tenant settings (a Fabric/Power BI administrator must change them). Applying each to a dedicated security group and adding your app to that group is the recommended pattern.
Tenant setting
Why it matters
Service principals can use Fabric / Power BI APIs
Required for the service principal to call the Power BI REST API at all. Without it, discovery and queries fail.
Service principals can access read-only admin APIs
Lets Bag of words read model schemas via the Admin (Scanner) API — the most reliable discovery path, and the only one that reads schema without per-model query permission.
Enhanced admin API responses with detailed metadata
Makes the Scanner API return table/column schema (datasetSchema). Strongly recommended, especially for DirectLake and Fabric default semantic models.
If the read-only admin APIs are not enabled, Bag of words falls back to a per-model DAX probe. That probe requires Build permission on each model and does not work for some DirectLake models — which is the most common reason a model appears in Power BI but is missing from the schema list. Enabling the two admin settings above resolves this for most tenants.
After enabling Enhanced admin API responses, models that have not been refreshed since the setting was turned on may still return no schema until their next refresh. Refresh such models once.
For the service principal, discovery only sees workspaces where it holds a workspace role — sharing a single report or dataset directly is not enough. With per-user authentication this is looser: a model shared directly with a user (Build permission) is also discovered, even when they have no workspace role at all. That item-level path is exactly what row-level security requires (see the RLS step).
1
Add the identity as a workspace Member or Contributor
In each Power BI workspace → Manage access → Add people or groups, add the service principal (or the security group containing it), and any per-user accounts, as Member or Contributor.
For the service principal relying on the DAX discovery fallback, Viewer is not enough — a Viewer cannot run executeQueries. Use Member or Contributor, or enable the read-only admin APIs above. Row-level security is the exception: a per-user identity that must be RLS-filtered should be Viewer or lower (Member/Contributor bypass RLS) — see the RLS step below.
2
Grant Build permission on the semantic models
Build is the permission that actually lets an identity run DAX (executeQueries) — Read alone is not enough. Workspace Member/Contributor typically confers Build; you can also grant it directly on the model (the model’s Manage permissions → Add user → Build). Direct Build with no workspace role is exactly what lets a per-user identity query a model under row-level security (see the next step).
3
(Row-level security) Use per-user auth and map each user into an RLS role
Row-level security is enforced against the querying identity, and two Microsoft rules decide the setup:
A service principal cannot query an RLS model at all. It cannot be added to an RLS role, so executeQueries returns 401 — even if the service principal is a workspace Member. Use Sign in with Microsoft (per-user) for any RLS-protected model; the service principal can still index non-RLS models in the same tenant.
An edit-level workspace role bypasses RLS. Admin, Member, and Contributor have edit permission, so RLS does not apply to them. For RLS to take effect a user must hold at most Viewer, or no workspace role with the model shared to them directly.
So the correct setup for an RLS model is, per user: Build permission on the model and membership in the appropriate RLS role, with no edit-level workspace role. Bag of words discovers and queries the model with that user’s own token, and Power BI filters the rows to their role.
Go to Settings → Data Sources → Add data source → Power BI and fill in the form. See Connecting a data source for the general flow.
Field
Required
Default
Notes
tenant_id
Yes
—
Azure AD Tenant ID (Directory ID) from the app’s Overview page.
client_id
Yes
—
Azure AD App Registration Client ID.
client_secret
Yes
—
Azure AD App Registration secret value you copied in Step 1.
oauth_client_id
No
—
App Registration Client ID used for user sign-in (authorization code flow). If blank, falls back to client_id.
oauth_client_secret
No
—
Secret for the user-sign-in app. If blank, falls back to client_secret.
workspaces
No
—
Workspace name(s) or ID(s), comma-separated, to limit discovery. If empty, all accessible workspaces are discovered.
1
Choose the authentication
Pick Service Principal (Azure AD) for a shared identity, or Sign in with Microsoft for per-user (delegated) access. The Sign in with Microsoft mode has no fields of its own — it reuses the app registration configured above.
2
Test the connection
Click Test Connection. Bag of words authenticates, lists workspaces, and probes a semantic model to verify query access. A model that is empty or not queryable does not fail the test — it reports that query access was verified.
3
Require user authentication (optional)
To force every user to sign in with their own Microsoft account instead of using the shared service principal, enable Require user authentication under the data source’s access settings. Each user then clicks Connect to sign in before they can query. See Authentication.
4
Select tables and save
After the connection indexes, choose which tables the AI agent may use, then save. See Data sources overview for managing tables, instructions, and access.
Once connected, the AI agent writes and runs DAX against your semantic models automatically. Tables are addressed as Dataset/Table, and relationships defined in the model are available for joins. You do not need to configure anything further to start asking questions.
With per-user authentication, a dashboard built on a Power BI model can be shared so that each viewer’s queries run under their own token. Power BI’s row-level security then filters the rows per person — one shared dashboard, and every viewer sees only what their RLS role allows. Configure this with Viewer run identity when sharing; see personalizing a dashboard.
View-as on a Power BI (delegated) source previews the target identity’s parameter binding but keeps your token, so it shows your source-level rows, not theirs. To confirm the exact rows another person gets under RLS, have them open the shared dashboard themselves.
Troubleshooting: a model is missing from the schema
If a semantic model is visible in app.powerbi.com but not selectable in Bag of words, work through these in order:
The identity is not a Member/Contributor of the workspace
For the service principal, discovery only sees workspaces where it has a role — add it as Member or Contributor, and note that content in My Workspace is not discovered. With per-user authentication this is looser: a model shared directly with the signed-in user (Build permission) is discovered even with no workspace role, so a direct dataset share is sufficient there — which is the setup RLS requires.
The model cannot be introspected (DirectLake / no Build / RLS)
Without the read-only admin APIs, schema comes from a DAX probe that needs Build permission and does not work for some DirectLake models. Enable Service principals can access read-only admin APIs + Enhanced admin API responses with detailed metadata in the tenant settings, and grant Build on the model. The connection’s indexing report lists models that were found but could not be read, with the reason.
The model was not refreshed after enabling enhanced metadata
The Scanner API returns no schema for models not refreshed since detailed-metadata scanning was enabled. Refresh the model once and re-index the connection.
A per-user model isn't showing for a signed-in user
With per-user authentication, the user must have completed Connect (Microsoft sign-in). Once signed in, models their own account can access become selectable even if the shared service principal cannot see them. If access was just changed in Power BI, note that Power BI caches user permissions — Bag of words refreshes them on sign-in and on Reload tables, so click Reload if a newly granted model hasn’t appeared yet.
A per-user model is discovered but the agent can't query it yet
A model that only a signed-in user can see (for example an RLS model the service principal cannot index) is added to the catalog but starts unselected. An admin or the data source owner must enable it on the Select tables step before the agent can query it. Until then it appears in the user’s table list but not in the agent’s context.
The workspaces filter is excluding it
If you set workspaces, only those workspaces are discovered. Clear it to discover everything, or add the workspace’s name/ID.
{"docsConfig":{"theme":"mint","$schema":"https://mintlify.com/docs.json","name":"Bag of words","colors":{"primary":"#0C0C15","light":"#fff","dark":"#0C0C15"},"logo":{"light":"https://mintcdn.com/bagofwords/WQHfvEO6F7pNQ6s3/assets/logo-128.png?fit=max\u0026auto=format\u0026n=WQHfvEO6F7pNQ6s3\u0026q=85\u0026s=fa5078d047623ff6f8fd004c91ae3bc3","dark":"https://mintcdn.com/bagofwords/WQHfvEO6F7pNQ6s3/assets/logo-128.png?fit=max\u0026auto=format\u0026n=WQHfvEO6F7pNQ6s3\u0026q=85\u0026s=fa5078d047623ff6f8fd004c91ae3bc3"},"favicon":"https://mintcdn.com/bagofwords/WQHfvEO6F7pNQ6s3/favicon.png?fit=max\u0026auto=format\u0026n=WQHfvEO6F7pNQ6s3\u0026q=85\u0026s=6788581b3499e0fdb8979f9e65ef17e3","background":{"color":{"light":"#fff","dark":"#0C0C15"}},"navbar":{"links":[{"href":"mailto:[email protected]","label":"Support"}]},"footer":{"socials":{"x":"https://x.com/bagofwords_com","github":"https://github.com/bagofwords1"}},"icons":{"library":"lucide"},"styling":{"latex":true},"navigation":{"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"bagofwords","hasAudioNarration":false,"actualSubdomain":"bagofwords","customDomains":["docs.bagofwords.com"],"gitSource":{"type":"github","owner":"bagofwords1","repo":"bagofdocs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6ad8ff3ae45690ec0368515f","collectionId":"297df5a2-1dec-4374-946f-a8ec6114a4cc","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6ad8ff3ae45690ec0368515f"},"entitlements":{},"buildId":"6ad8ff3ae45690ec0368515f","clientVersion":"0.0.3964","searchFilterCounts":[{"language":"default","tag":"Connect Any Data","count":44},{"language":"default","tag":"Connectors","count":33},{"language":"default","tag":"Guides","count":20},{"language":"default","tag":"Work With Agents","count":9},{"language":"default","tag":"Channels","count":7},{"language":"default","tag":"Agents","count":5},{"language":"default","tag":"Observe and Govern","count":5},{"language":"default","tag":"Custom Queries and Row-Level Security","count":4},{"language":"default","tag":"Get Started","count":4},{"language":"default","tag":"Open Source and Enterprise","count":2}]},"assistantConfig":{},"hasChatPermissions":false,"hasWebMcpPermissions":false,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-v7-file-tree","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"pause-universal-search-reconcile","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"per-site-search-delta","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-private-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-nav-generations","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"byok-sep-25","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-agent-model-picker","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-updated-9-15","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"isAtlas":false,"googleFontsInlined":false},"customCss":[],"page":{"pagePath":"data-sources/connectors/power-bi","slug":"/data-sources/connectors/power-bi","pageMetadata":{"title":"Power BI","description":"Connect Power BI semantic models to Bag of words and query them with DAX","icon":"chart-column","href":"/data-sources/connectors/power-bi"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Connect Power BI semantic models to Bag of words and query them with DAX\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];function _createMdxContent(props){const _components={a:`a`,code:`code`,em:`em`,li:`li`,ol:`ol`,p:`p`,pre:`pre`,span:`span`,strong:`strong`,tbody:`tbody`,td:`td`,th:`th`,thead:`thead`,tr:`tr`,ul:`ul`,..._provideComponents(),...props.components},{Accordion,AccordionGroup,Card,CardGroup,CodeBlock,Heading,Note,Step,Steps,Table,Warning}=_components;if(!Accordion)_missingMdxReference(`Accordion`,true);if(!AccordionGroup)_missingMdxReference(`AccordionGroup`,true);if(!Card)_missingMdxReference(`Card`,true);if(!CardGroup)_missingMdxReference(`CardGroup`,true);if(!CodeBlock)_missingMdxReference(`CodeBlock`,true);if(!Heading)_missingMdxReference(`Heading`,true);if(!Note)_missingMdxReference(`Note`,true);if(!Step)_missingMdxReference(`Step`,true);if(!Steps)_missingMdxReference(`Steps`,true);if(!Table)_missingMdxReference(`Table`,true);if(!Warning)_missingMdxReference(`Warning`,true);return _jsxs(_Fragment,{children:[_jsxs(_components.p,{children:[`Bag of words connects to the `,_jsx(_components.strong,{children:`Power BI REST API`}),` to auto-discover the workspaces, semantic models (datasets), tables, and reports an identity can access, and to run `,_jsx(_components.strong,{children:`DAX`}),` queries against them. This page covers everything you set up on the Microsoft side (app registration, tenant settings, workspace membership, permissions) and how to enter it in Bag of words.`]}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[`The Power BI connector is part of the `,_jsx(_components.strong,{children:`Enterprise Edition`}),`. Per-user (delegated) authentication in particular requires an Enterprise license.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`how-it-works`,children:`How it works`}),`\n`,_jsxs(_components.p,{children:[`Bag of words authenticates to Power BI as an `,_jsx(_components.strong,{children:`Azure AD (Microsoft Entra ID) application`}),`, then:`]}),`\n`,_jsxs(_components.ol,{children:[`\n`,_jsxs(_components.li,{children:[`Lists the `,_jsx(_components.strong,{children:`workspaces`}),` the identity belongs to (`,_jsx(_components.code,{children:`GET /v1.0/myorg/groups`}),`).`]}),`\n`,_jsxs(_components.li,{children:[`Lists the `,_jsx(_components.strong,{children:`semantic models`}),` (datasets) and `,_jsx(_components.strong,{children:`reports`}),` in each workspace.`]}),`\n`,_jsxs(_components.li,{children:[`Reads each model’s `,_jsx(_components.strong,{children:`tables and columns`}),` so the AI agent knows the schema. Discovery uses the `,_jsx(_components.strong,{children:`read-only Admin (Scanner) API`}),` when available, and falls back to a DAX `,_jsx(_components.code,{children:`COLUMNSTATISTICS()`}),` probe per model otherwise.`]}),`\n`,_jsxs(_components.li,{children:[`Runs `,_jsx(_components.strong,{children:`DAX`}),` queries at request time (`,_jsx(_components.code,{children:`POST .../executeQueries`}),`).`]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`Each Power BI table is exposed to the agent as a schema table named `,_jsx(_components.code,{children:`Dataset/Table`}),` (for example `,_jsx(_components.code,{children:`Sales Model/Customers`}),`).`]}),`\n`,_jsx(Heading,{level:`2`,id:`authentication-modes`,children:`Authentication modes`}),`\n`,_jsx(_components.p,{children:`Power BI supports two authentication modes. You can use either, or both.`}),`\n`,_jsxs(CardGroup,{cols:2,children:[_jsx(Card,{title:`Service Principal (Azure AD)`,icon:`bot`,children:_jsxs(_components.p,{children:[`A single shared application identity (a `,_jsx(_components.strong,{children:`system`}),` credential) discovers and queries Power BI for everyone. Simplest to operate. Best when all users may see the same models.`]})}),_jsx(Card,{title:`Sign in with Microsoft (per-user)`,icon:`user-lock`,children:_jsxs(_components.p,{children:[`Each user signs in with their own Microsoft account (OAuth / on-behalf-of). Discovery and queries run with that user’s own permissions, so each person sees only what they are allowed to. Requires Enterprise — see `,_jsx(_components.a,{href:`/data-sources/authentication`,children:`Authentication`}),`.`]})})]}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[`With per-user authentication, a semantic model is selectable for a user as long as `,_jsx(_components.strong,{children:`their own`}),` account can see it — even if the shared service principal cannot. Usage and instructions are still tracked at the organization level per model.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`before-you-start`,children:`Before you start`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`A `,_jsx(_components.strong,{children:`Microsoft Entra ID (Azure AD)`}),` tenant where you can create an app registration.`]}),`\n`,_jsxs(_components.li,{children:[`A `,_jsx(_components.strong,{children:`Fabric / Power BI administrator`}),` who can change tenant settings.`]}),`\n`,_jsx(_components.li,{children:`Workspace admin rights on the workspaces you want to expose, so you can add the identity as a Member or Contributor.`}),`\n`,_jsxs(_components.li,{children:[`Outbound network access from Bag of words to `,_jsx(_components.code,{children:`https://api.powerbi.com`}),` and `,_jsx(_components.code,{children:`https://login.microsoftonline.com`}),`.`]}),`\n`]}),`\n`,_jsx(Heading,{level:`2`,id:`step-1-—-register-an-azure-ad-application`,children:`Step 1 — Register an Azure AD application`}),`\n`,_jsxs(_components.p,{children:[`In the `,_jsx(_components.a,{href:`https://portal.azure.com`,children:`Azure Portal`}),` → `,_jsx(_components.strong,{children:`Microsoft Entra ID`}),` → `,_jsx(_components.strong,{children:`App registrations`}),` → `,_jsx(_components.strong,{children:`New registration`}),`.`]}),`\n`,_jsxs(Steps,{children:[_jsx(Step,{title:`Create the app registration`,children:_jsxs(_components.p,{children:[`Give it a name (e.g. `,_jsx(_components.code,{children:`Bag of words - Power BI`}),`). Under `,_jsx(_components.strong,{children:`Supported account types`}),`, choose `,_jsx(_components.strong,{children:`Accounts in this organizational directory only`}),` unless you specifically need multi-tenant.`]})}),_jsx(Step,{title:`Copy the Tenant ID and Client ID`,children:_jsxs(_components.p,{children:[`From the app’s `,_jsx(_components.strong,{children:`Overview`}),` page, copy the `,_jsx(_components.strong,{children:`Directory (tenant) ID`}),` and the `,_jsx(_components.strong,{children:`Application (client) ID`}),`. You will paste these into Bag of words.`]})}),_jsx(Step,{title:`Create a client secret`,children:_jsxs(_components.p,{children:[`Go to `,_jsx(_components.strong,{children:`Certificates \u0026 secrets`}),` → `,_jsx(_components.strong,{children:`New client secret`}),`. Copy the `,_jsx(_components.strong,{children:`secret value`}),` immediately (it is only shown once). This is the `,_jsx(_components.code,{children:`client_secret`}),` field in Bag of words.`]})}),_jsx(Step,{title:`Add API permissions`,children:_jsxs(_components.p,{children:[`Under `,_jsx(_components.strong,{children:`API permissions`}),` → `,_jsx(_components.strong,{children:`Add a permission`}),` → `,_jsx(_components.strong,{children:`Power BI Service`}),`, add the delegated `,_jsx(_components.strong,{children:`Tenant.Read.All`}),` (or the read scopes your policy allows). For per-user sign-in, keep the delegated `,_jsx(_components.strong,{children:`profile`}),`, `,_jsx(_components.strong,{children:`email`}),`, and `,_jsx(_components.strong,{children:`openid`}),` scopes. Grant admin consent for the directory.`]})}),_jsxs(Step,{title:`(Per-user only) Add the redirect URI`,children:[_jsxs(_components.p,{children:[`For `,_jsx(_components.strong,{children:`Sign in with Microsoft`}),`, add a `,_jsx(_components.strong,{children:`Web`}),` redirect URI under `,_jsx(_components.strong,{children:`Authentication`}),`:`]}),_jsx(CodeBlock,{filename:``,numberOfLines:`2`,language:`text`,children:_jsx(_components.pre,{className:`shiki shiki-themes github-light-default dark-plus`,style:{backgroundColor:`#ffffff`,\"--shiki-dark-bg\":`#0B0C0E`,color:`#1f2328`,\"--shiki-dark\":`#D4D4D4`},language:`text`,children:_jsxs(_components.code,{language:`text`,numberOfLines:`2`,children:[_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{children:`https://\u003cyour-bow-host>/api/connections/oauth/callback`})}),`\n`,_jsx(_components.span,{className:`line`,children:_jsx(_components.span,{})})]})})}),_jsxs(_components.p,{children:[`Replace `,_jsx(_components.code,{children:`\u003cyour-bow-host>`}),` with your Bag of words base URL. Enable `,_jsx(_components.strong,{children:`offline_access`}),` so the token can be refreshed.`]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`step-2-—-enable-the-power-bi-tenant-settings`,children:`Step 2 — Enable the Power BI tenant settings`}),`\n`,_jsxs(_components.p,{children:[`These live in the `,_jsx(_components.strong,{children:`Power BI / Fabric Admin portal`}),` → `,_jsx(_components.strong,{children:`Tenant settings`}),` (a Fabric/Power BI administrator must change them). Applying each to a dedicated `,_jsx(_components.strong,{children:`security group`}),` and adding your app to that group is the recommended pattern.`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{style:{textAlign:`left`},children:`Tenant setting`}),_jsx(_components.th,{style:{textAlign:`left`},children:`Why it matters`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.strong,{children:`Service principals can use Fabric / Power BI APIs`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`Required for the service principal to call the Power BI REST API at all. Without it, discovery and queries fail.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.strong,{children:`Service principals can access read-only admin APIs`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`Lets Bag of words read model schemas via the Admin (Scanner) API — the most reliable discovery path, and the only one that reads schema without per-model query permission.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.strong,{children:`Enhanced admin API responses with detailed metadata`})}),_jsxs(_components.td,{style:{textAlign:`left`},children:[`Makes the Scanner API return table/column schema (datasetSchema). Strongly recommended, especially for `,_jsx(_components.strong,{children:`DirectLake`}),` and Fabric default semantic models.`]})]})]})]}),`\n`,_jsx(Warning,{children:_jsxs(_components.p,{children:[`If the read-only admin APIs are `,_jsx(_components.strong,{children:`not`}),` enabled, Bag of words falls back to a per-model DAX probe. That probe requires `,_jsx(_components.strong,{children:`Build`}),` permission on each model and does not work for some `,_jsx(_components.strong,{children:`DirectLake`}),` models — which is the most common reason a model appears in Power BI but is missing from the schema list. Enabling the two admin settings above resolves this for most tenants.`]})}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[`After enabling `,_jsx(_components.strong,{children:`Enhanced admin API responses`}),`, models that have not been refreshed since the setting was turned on may still return no schema until their next refresh. Refresh such models once.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`step-3-—-grant-workspace-and-dataset-access`,children:`Step 3 — Grant workspace and dataset access`}),`\n`,_jsxs(_components.p,{children:[`For the `,_jsx(_components.strong,{children:`service principal`}),`, discovery only sees workspaces where it holds a workspace `,_jsx(_components.strong,{children:`role`}),` — sharing a single report or dataset directly is not enough. With `,_jsx(_components.strong,{children:`per-user`}),` authentication this is looser: a model shared `,_jsx(_components.strong,{children:`directly`}),` with a user (Build permission) is also discovered, even when they have no workspace role at all. That item-level path is exactly what row-level security requires (see the RLS step).`]}),`\n`,_jsxs(Steps,{children:[_jsxs(Step,{title:`Add the identity as a workspace Member or Contributor`,children:[_jsxs(_components.p,{children:[`In each Power BI workspace → `,_jsx(_components.strong,{children:`Manage access`}),` → `,_jsx(_components.strong,{children:`Add people or groups`}),`, add the service principal (or the security group containing it), and any per-user accounts, as `,_jsx(_components.strong,{children:`Member`}),` or `,_jsx(_components.strong,{children:`Contributor`}),`.`]}),_jsx(Warning,{children:_jsxs(_components.p,{children:[`For the `,_jsx(_components.strong,{children:`service principal`}),` relying on the DAX discovery fallback, `,_jsx(_components.strong,{children:`Viewer is not enough`}),` — a Viewer cannot run `,_jsx(_components.code,{children:`executeQueries`}),`. Use `,_jsx(_components.strong,{children:`Member`}),` or `,_jsx(_components.strong,{children:`Contributor`}),`, or enable the read-only admin APIs above. `,_jsx(_components.strong,{children:`Row-level security is the exception:`}),` a per-user identity that must be RLS-filtered should be `,_jsx(_components.strong,{children:`Viewer or lower`}),` (Member/Contributor bypass RLS) — see the RLS step below.`]})})]}),_jsx(Step,{title:`Grant Build permission on the semantic models`,children:_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`Build`}),` is the permission that actually lets an identity run DAX (`,_jsx(_components.code,{children:`executeQueries`}),`) — Read alone is not enough. Workspace Member/Contributor typically confers Build; you can also grant it `,_jsx(_components.strong,{children:`directly on the model`}),` (the model’s `,_jsx(_components.strong,{children:`Manage permissions → Add user → Build`}),`). Direct Build with no workspace role is exactly what lets a per-user identity query a model under row-level security (see the next step).`]})}),_jsxs(Step,{title:`(Row-level security) Use per-user auth and map each user into an RLS role`,children:[_jsxs(_components.p,{children:[`Row-level security is enforced against the `,_jsx(_components.strong,{children:`querying identity`}),`, and two Microsoft rules decide the setup:`]}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`A service principal cannot query an RLS model at all.`}),` It cannot be added to an RLS role, so `,_jsx(_components.code,{children:`executeQueries`}),` returns `,_jsx(_components.code,{children:`401`}),` — even if the service principal is a workspace Member. Use `,_jsx(_components.strong,{children:`Sign in with Microsoft`}),` (per-user) for any RLS-protected model; the service principal can still index non-RLS models in the same tenant.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`An edit-level workspace role bypasses RLS.`}),` Admin, Member, and Contributor have edit permission, so RLS does `,_jsx(_components.strong,{children:`not`}),` apply to them. For RLS to take effect a user must hold at most `,_jsx(_components.strong,{children:`Viewer`}),`, or no workspace role with the model shared to them directly.`]}),`\n`]}),_jsxs(_components.p,{children:[`So the correct setup for an RLS model is, per user: `,_jsx(_components.strong,{children:`Build`}),` permission on the model `,_jsx(_components.strong,{children:`and`}),` membership in the appropriate `,_jsx(_components.strong,{children:`RLS role`}),`, with `,_jsx(_components.strong,{children:`no`}),` edit-level workspace role. Bag of words discovers and queries the model with that user’s own token, and Power BI filters the rows to their role.`]})]})]}),`\n`,_jsx(Heading,{level:`2`,id:`connect-in-bag-of-words`,children:`Connect in Bag of words`}),`\n`,_jsxs(_components.p,{children:[`Go to `,_jsx(_components.strong,{children:`Settings → Data Sources → Add data source → Power BI`}),` and fill in the form. See `,_jsx(_components.a,{href:`/data-sources/connecting`,children:`Connecting a data source`}),` for the general flow.`]}),`\n`,_jsxs(Table,{children:[_jsx(_components.thead,{children:_jsxs(_components.tr,{children:[_jsx(_components.th,{style:{textAlign:`left`},children:`Field`}),_jsx(_components.th,{style:{textAlign:`left`},children:`Required`}),_jsx(_components.th,{style:{textAlign:`left`},children:`Default`}),_jsx(_components.th,{style:{textAlign:`left`},children:`Notes`})]})}),_jsxs(_components.tbody,{children:[_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.code,{children:`tenant_id`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`Yes`}),_jsx(_components.td,{style:{textAlign:`left`},children:`—`}),_jsx(_components.td,{style:{textAlign:`left`},children:`Azure AD Tenant ID (Directory ID) from the app’s Overview page.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.code,{children:`client_id`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`Yes`}),_jsx(_components.td,{style:{textAlign:`left`},children:`—`}),_jsx(_components.td,{style:{textAlign:`left`},children:`Azure AD App Registration Client ID.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.code,{children:`client_secret`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`Yes`}),_jsx(_components.td,{style:{textAlign:`left`},children:`—`}),_jsx(_components.td,{style:{textAlign:`left`},children:`Azure AD App Registration secret value you copied in Step 1.`})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.code,{children:`oauth_client_id`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`No`}),_jsx(_components.td,{style:{textAlign:`left`},children:`—`}),_jsxs(_components.td,{style:{textAlign:`left`},children:[`App Registration Client ID used for `,_jsx(_components.strong,{children:`user sign-in`}),` (authorization code flow). If blank, falls back to `,_jsx(_components.code,{children:`client_id`}),`.`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.code,{children:`oauth_client_secret`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`No`}),_jsx(_components.td,{style:{textAlign:`left`},children:`—`}),_jsxs(_components.td,{style:{textAlign:`left`},children:[`Secret for the user-sign-in app. If blank, falls back to `,_jsx(_components.code,{children:`client_secret`}),`.`]})]}),_jsxs(_components.tr,{children:[_jsx(_components.td,{style:{textAlign:`left`},children:_jsx(_components.code,{children:`workspaces`})}),_jsx(_components.td,{style:{textAlign:`left`},children:`No`}),_jsx(_components.td,{style:{textAlign:`left`},children:`—`}),_jsxs(_components.td,{style:{textAlign:`left`},children:[`Workspace name(s) or ID(s), comma-separated, to limit discovery. If empty, `,_jsx(_components.strong,{children:`all`}),` accessible workspaces are discovered.`]})]})]})]}),`\n`,_jsxs(Steps,{children:[_jsx(Step,{title:`Choose the authentication`,children:_jsxs(_components.p,{children:[`Pick `,_jsx(_components.strong,{children:`Service Principal (Azure AD)`}),` for a shared identity, or `,_jsx(_components.strong,{children:`Sign in with Microsoft`}),` for per-user (delegated) access. The `,_jsx(_components.strong,{children:`Sign in with Microsoft`}),` mode has no fields of its own — it reuses the app registration configured above.`]})}),_jsx(Step,{title:`Test the connection`,children:_jsxs(_components.p,{children:[`Click `,_jsx(_components.strong,{children:`Test Connection`}),`. Bag of words authenticates, lists workspaces, and probes a semantic model to verify query access. A model that is empty or not queryable does not fail the test — it reports that query access was verified.`]})}),_jsx(Step,{title:`Require user authentication (optional)`,children:_jsxs(_components.p,{children:[`To force every user to sign in with their own Microsoft account instead of using the shared service principal, enable `,_jsx(_components.strong,{children:`Require user authentication`}),` under the data source’s access settings. Each user then clicks `,_jsx(_components.strong,{children:`Connect`}),` to sign in before they can query. See `,_jsx(_components.a,{href:`/data-sources/authentication`,children:`Authentication`}),`.`]})}),_jsx(Step,{title:`Select tables and save`,children:_jsxs(_components.p,{children:[`After the connection indexes, choose which tables the AI agent may use, then save. See `,_jsx(_components.a,{href:`/data-sources/overview`,children:`Data sources overview`}),` for managing tables, instructions, and access.`]})})]}),`\n`,_jsx(Heading,{level:`2`,id:`querying-with-dax`,children:`Querying with DAX`}),`\n`,_jsxs(_components.p,{children:[`Once connected, the AI agent writes and runs `,_jsx(_components.strong,{children:`DAX`}),` against your semantic models automatically. Tables are addressed as `,_jsx(_components.code,{children:`Dataset/Table`}),`, and relationships defined in the model are available for joins. You do not need to configure anything further to start asking questions.`]}),`\n`,_jsx(Heading,{level:`2`,id:`personalized-dashboards-over-power-bi-rls`,children:`Personalized dashboards over Power BI RLS`}),`\n`,_jsxs(_components.p,{children:[`With `,_jsx(_components.strong,{children:`per-user`}),` authentication, a dashboard built on a Power BI model can be shared so that each viewer’s queries run under `,_jsx(_components.strong,{children:`their own`}),` token. Power BI’s row-level security then filters the rows per person — one shared dashboard, and every viewer sees only what their RLS role allows. Configure this with `,_jsx(_components.strong,{children:`Viewer`}),` run identity when sharing; see `,_jsx(_components.a,{href:`/using-bow/dashboards#personalize-a-dashboard-per-viewer`,children:`personalizing a dashboard`}),`.`]}),`\n`,_jsx(Note,{children:_jsxs(_components.p,{children:[_jsx(_components.strong,{children:`View-as`}),` on a Power BI (delegated) source previews the target identity’s parameter binding but keeps `,_jsx(_components.em,{children:`your`}),` token, so it shows your source-level rows, not theirs. To confirm the exact rows another person gets under RLS, have them open the shared dashboard themselves.`]})}),`\n`,_jsx(Heading,{level:`2`,id:`troubleshooting-a-model-is-missing-from-the-schema`,children:`Troubleshooting: a model is missing from the schema`}),`\n`,_jsxs(_components.p,{children:[`If a semantic model is visible in `,_jsx(_components.code,{children:`app.powerbi.com`}),` but not selectable in Bag of words, work through these in order:`]}),`\n`,_jsxs(AccordionGroup,{children:[_jsx(Accordion,{title:`The identity is not a Member/Contributor of the workspace`,id:`the-identity-is-not-a-member-contributor-of-the-workspace`,children:_jsxs(_components.p,{children:[`For the `,_jsx(_components.strong,{children:`service principal`}),`, discovery only sees workspaces where it has a role — add it as `,_jsx(_components.strong,{children:`Member`}),` or `,_jsx(_components.strong,{children:`Contributor`}),`, and note that content in `,_jsx(_components.strong,{children:`My Workspace`}),` is not discovered. With `,_jsx(_components.strong,{children:`per-user`}),` authentication this is looser: a model shared directly with the signed-in user (Build permission) is discovered even with no workspace role, so a direct dataset share `,_jsx(_components.strong,{children:`is`}),` sufficient there — which is the setup RLS requires.`]})}),_jsx(Accordion,{title:`The model cannot be introspected (DirectLake / no Build / RLS)`,id:`the-model-cannot-be-introspected-directlake-no-build-rls`,children:_jsxs(_components.p,{children:[`Without the read-only admin APIs, schema comes from a DAX probe that needs `,_jsx(_components.strong,{children:`Build`}),` permission and does not work for some `,_jsx(_components.strong,{children:`DirectLake`}),` models. Enable `,_jsx(_components.strong,{children:`Service principals can access read-only admin APIs`}),` + `,_jsx(_components.strong,{children:`Enhanced admin API responses with detailed metadata`}),` in the tenant settings, and grant `,_jsx(_components.strong,{children:`Build`}),` on the model. The connection’s indexing report lists models that were found but could not be read, with the reason.`]})}),_jsx(Accordion,{title:`The model was not refreshed after enabling enhanced metadata`,id:`the-model-was-not-refreshed-after-enabling-enhanced-metadata`,children:_jsx(_components.p,{children:`The Scanner API returns no schema for models not refreshed since detailed-metadata scanning was enabled. Refresh the model once and re-index the connection.`})}),_jsx(Accordion,{title:`A per-user model isn't showing for a signed-in user`,id:`a-per-user-model-isnt-showing-for-a-signed-in-user`,children:_jsxs(_components.p,{children:[`With per-user authentication, the user must have completed `,_jsx(_components.strong,{children:`Connect`}),` (Microsoft sign-in). Once signed in, models their own account can access become selectable even if the shared service principal cannot see them. If access was `,_jsx(_components.strong,{children:`just changed`}),` in Power BI, note that Power BI caches user permissions — Bag of words refreshes them on sign-in and on `,_jsx(_components.strong,{children:`Reload tables`}),`, so click `,_jsx(_components.strong,{children:`Reload`}),` if a newly granted model hasn’t appeared yet.`]})}),_jsx(Accordion,{title:`A per-user model is discovered but the agent can't query it yet`,id:`a-per-user-model-is-discovered-but-the-agent-cant-query-it-yet`,children:_jsxs(_components.p,{children:[`A model that only a signed-in user can see (for example an RLS model the service principal cannot index) is added to the catalog but starts `,_jsx(_components.strong,{children:`unselected`}),`. An admin or the data source owner must enable it on the `,_jsx(_components.strong,{children:`Select tables`}),` step before the agent can query it. Until then it appears in the user’s table list but not in the agent’s context.`]})}),_jsx(Accordion,{title:`The workspaces filter is excluding it`,id:`the-workspaces-filter-is-excluding-it`,children:_jsxs(_components.p,{children:[`If you set `,_jsx(_components.code,{children:`workspaces`}),`, only those workspaces are discovered. Clear it to discover everything, or add the workspace’s name/ID.`]})})]}),`\n`,_jsx(Heading,{level:`2`,id:`reference`,children:`Reference`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Service principal scope`}),`: `,_jsx(_components.code,{children:`https://analysis.windows.net/powerbi/api/.default`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Per-user (delegated) scope`}),`: `,_jsx(_components.code,{children:`https://analysis.windows.net/powerbi/api/.default offline_access`})]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`OAuth redirect URI`}),`: `,_jsx(_components.code,{children:`https://\u003cyour-bow-host>/api/connections/oauth/callback`})]}),`\n`]}),`\n`,_jsxs(_components.p,{children:[`For the on-premises product, see `,_jsx(_components.a,{href:`/data-sources/connectors/power-bi-report-server`,children:`Power BI Report Server`}),`.`]})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Power BI","description":"Connect Power BI semantic models to Bag of words and query them with DAX","icon":"chart-column","href":"/data-sources/connectors/power-bi"}}},"mdxSourceWithNoJs":null},"mdxExtracts":{"tableOfContents":[{"title":"How it works","slug":"how-it-works","depth":2,"children":[]},{"title":"Authentication modes","slug":"authentication-modes","depth":2,"children":[]},{"title":"Before you start","slug":"before-you-start","depth":2,"children":[]},{"title":"Step 1 — Register an Azure AD application","slug":"step-1-—-register-an-azure-ad-application","depth":2,"children":[]},{"title":"Step 2 — Enable the Power BI tenant settings","slug":"step-2-—-enable-the-power-bi-tenant-settings","depth":2,"children":[]},{"title":"Step 3 — Grant workspace and dataset access","slug":"step-3-—-grant-workspace-and-dataset-access","depth":2,"children":[]},{"title":"Connect in Bag of words","slug":"connect-in-bag-of-words","depth":2,"children":[]},{"title":"Querying with DAX","slug":"querying-with-dax","depth":2,"children":[]},{"title":"Personalized dashboards over Power BI RLS","slug":"personalized-dashboards-over-power-bi-rls","depth":2,"children":[]},{"title":"Troubleshooting: a model is missing from the schema","slug":"troubleshooting-a-model-is-missing-from-the-schema","depth":2,"children":[]},{"title":"Reference","slug":"reference","depth":2,"children":[]}],"codeExamples":{}},"jsLinks":[],"subdomain":"bagofwords","siteName":"Bag of words","title":"Power BI - Bag of words","navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-08-23T15:48:33.907Z","originalFileLocation":"data-sources/connectors/power-bi.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"system\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Connect Power BI semantic models to Bag of words and query them with DAX\">\u003cmeta data-mint-head name=\"application-name\" content=\"Bag of words\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Power BI - Bag of words\">\u003cmeta data-mint-head property=\"og:description\" content=\"Connect Power BI semantic models to Bag of words and query them with DAX\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"Bag of words\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.bagofwords.com/data-sources/connectors/power-bi\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://bagofwords.mintlify.app/_mintlify/api/og/v2?division=Connectors\u0026amp;title=Power+BI\u0026amp;description=Connect+Power+BI+semantic+models+to+Bag+of+words+and+query+them+with+DAX\u0026amp;theme=1b730fc2015ef354744bd263\u0026amp;signature=f07e12565641db43cf1ff0a6d1dadaed0c1e6986abc7aca5688c7fff250eb458\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Power BI - Bag of words\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Connect Power BI semantic models to Bag of words and query them with DAX\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://bagofwords.mintlify.app/_mintlify/api/og/v2?division=Connectors\u0026amp;title=Power+BI\u0026amp;description=Connect+Power+BI+semantic+models+to+Bag+of+words+and+query+them+with+DAX\u0026amp;theme=1b730fc2015ef354744bd263\u0026amp;signature=f07e12565641db43cf1ff0a6d1dadaed0c1e6986abc7aca5688c7fff250eb458\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/bagofwords/0eV4PLcEOWgaa_uR/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"Bag of words\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#0C0C15\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.bagofwords.com/data-sources/connectors/power-bi\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/data-sources/connectors/power-bi.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.bagofwords.com/#organization\",\"name\":\"Bag of words\",\"url\":\"https://docs.bagofwords.com\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/bagofwords/WQHfvEO6F7pNQ6s3/assets/logo-128.png?fit=max\u0026auto=format\u0026n=WQHfvEO6F7pNQ6s3\u0026q=85\u0026s=fa5078d047623ff6f8fd004c91ae3bc3\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.bagofwords.com#website\",\"name\":\"Bag of words\",\"url\":\"https://docs.bagofwords.com\",\"publisher\":{\"@id\":\"https://docs.bagofwords.com/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi#webpage\",\"url\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi\",\"name\":\"Power BI\",\"description\":\"Connect Power BI semantic models to Bag of words and query them with DAX\",\"dateModified\":\"2026-08-23T15:48:33.907Z\",\"isPartOf\":{\"@id\":\"https://docs.bagofwords.com#website\"},\"breadcrumb\":{\"@id\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi#breadcrumb\"}},{\"@type\":\"BreadcrumbList\",\"@id\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Connect Any Data\",\"item\":\"https://docs.bagofwords.com/data-sources/overview\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Connectors\",\"item\":\"https://docs.bagofwords.com/data-sources/connectors/databases\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Power BI\",\"item\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi\"}]},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi#article\",\"headline\":\"Power BI\",\"name\":\"Power BI\",\"description\":\"Connect Power BI semantic models to Bag of words and query them with DAX\",\"url\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi\",\"mainEntityOfPage\":{\"@id\":\"https://docs.bagofwords.com/data-sources/connectors/power-bi#webpage\"},\"dateModified\":\"2026-08-23T15:48:33.907Z\",\"publisher\":{\"@id\":\"https://docs.bagofwords.com/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.bagofwords.com#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"Bag of words\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"Bag of words","nav":{"global":null,"groups":[{"group":"Get Started","pages":[{"title":"Bag of words","href":"/introduction"},{"title":"Quickstart","icon":"rocket","href":"/quickstart"},{"title":"Deployment Options","icon":"server","href":"/install"},{"title":"Sign-in and SSO","icon":"key-round","href":"/sign-in-and-sso"}]},{"group":"Work With Agents","pages":[{"title":"Work with agents","icon":"messages-square","href":"/work-with-agents"},{"title":"Chat with your data","icon":"bot","href":"/using-bow/reports"},{"title":"Dashboard artifacts","icon":"chart-pie","href":"/using-bow/dashboards"},{"title":"Build apps with artifacts","sidebarTitle":"Build apps","icon":"app-window","href":"/using-bow/artifact-apps"},{"title":"Sub-agents","sidebarTitle":"Sub-agents","icon":"sitemap","href":"/using-bow/sub-agents"},{"title":"Parameterized queries","icon":"sliders","sidebarTitle":"Parameterized queries","href":"/using-bow/parameterized-queries"},{"title":"MCP","icon":"plug","href":"/using-bow/mcp"},{"title":"BOW for Excel","icon":"file-spreadsheet","href":"/using-bow/excel"},{"title":"AI Settings","icon":"cog","href":"/using-bow/settings"}]},{"group":"Agents","pages":[{"title":"Agents","icon":"bot","href":"/agents/overview"},{"title":"Instructions and knowledge","icon":"book-open","href":"/agents/instructions"},{"title":"Train an agent","sidebarTitle":"Train an agent","icon":"graduation-cap","href":"/agents/train-an-agent"},{"title":"Evals and self-improvement","icon":"badge-check","href":"/agents/evals"},{"title":"Check-ins and overnight learning","sidebarTitle":"Self-improvement","icon":"moon","href":"/agents/self-improvement"},{"title":"Lists","icon":"list","href":"/agents/lists"}]},{"group":"Users","pages":[]},{"group":"Guides","pages":[{"title":"Guides","sidebarTitle":"All guides","icon":"list","href":"/guides/overview"},{"title":"Train an agent","sidebarTitle":"Train an agent","icon":"graduation-cap","href":"/agents/train-an-agent"},{"title":"Tips for training an agent","sidebarTitle":"Training tips","icon":"lightbulb","href":"/guides/training-tips"},{"title":"Create a demo dataset","sidebarTitle":"Create a demo dataset","icon":"database","href":"/guides/demo-dataset"},{"title":"Root cause analysis across observability tools","sidebarTitle":"Root cause analysis","icon":"magnifying-glass-chart","href":"/guides/root-cause-analysis"},{"title":"Root cause analysis with sub-agents","sidebarTitle":"Root cause analysis with sub-agents","icon":"magnifying-glass-chart","href":"/guides/root-cause-analysis-sub-agents"},{"title":"Build a dashboard with parameterized queries","sidebarTitle":"Parameterized dashboards","icon":"sliders-horizontal","href":"/guides/parameterized-dashboard"},{"title":"Build a dashboard with state and saved data","sidebarTitle":"Dashboards with saved data","icon":"database-zap","href":"/guides/stateful-dashboard"},{"title":"Create a scheduled task for recurring analysis","sidebarTitle":"Scheduled tasks","icon":"calendar-clock","href":"/guides/scheduled-tasks"},{"title":"Run an automated analysis from an external trigger","sidebarTitle":"External triggers","icon":"webhook","href":"/guides/external-triggers"},{"title":"Test an agent with evals","sidebarTitle":"Test with evals","icon":"badge-check","href":"/guides/evals"},{"title":"Analyze your agents' usage in chat","sidebarTitle":"Usage analysis in chat","icon":"chart-no-axes-combined","href":"/guides/usage-analysis"},{"title":"Use saved queries for faster, more reliable answers","sidebarTitle":"Saved queries","icon":"bookmark","href":"/guides/saved-queries"},{"title":"Route questions to the right agent","sidebarTitle":"Route to the right agent","icon":"route","href":"/guides/agent-routing"},{"title":"Analyze documents with Lists","sidebarTitle":"Lists for documents","icon":"file-search","href":"/guides/lists-documents"},{"title":"Use BOW from Claude or another MCP client","sidebarTitle":"BOW from Claude (MCP)","icon":"plug","href":"/guides/mcp-clients"},{"title":"Git Workflow","icon":"git","href":"/guides/git"},{"title":"Embed BOW Chat","icon":"code-2","sidebarTitle":"Embed BOW Chat","href":"/guides/embed"},{"title":"External Entra applications","icon":"key","href":"/guides/external-entra-applications"},{"title":"Completions API","sidebarTitle":"Completions API","icon":"terminal","href":"/guides/completions-api"}]},{"group":"Connect Any Data","pages":[{"title":"Data Sources Overview","icon":"database","href":"/data-sources/overview"},{"title":"Connecting a Data Source","icon":"circle-plus","href":"/data-sources/connecting"},{"title":"Authentication and Access","icon":"lock","href":"/data-sources/authentication"},{"group":"Connectors","expanded":false,"pages":[{"title":"SQL databases","icon":"database","href":"/data-sources/connectors/databases"},{"title":"Snowflake","icon":"snowflake","href":"/data-sources/connectors/snowflake"},{"title":"Google BigQuery","icon":"cloud","href":"/data-sources/connectors/bigquery"},{"title":"Microsoft SQL Server","icon":"server","href":"/data-sources/connectors/sql-server"},{"title":"SAP","description":"Connect SAP HANA, SAP BW, SAP Datasphere, and SAP BusinessObjects to Bag of words","icon":"database","href":"/data-sources/connectors/sap"},{"title":"Power BI","icon":"chart-column","href":"/data-sources/connectors/power-bi"},{"title":"Power BI Report Server","description":"Connect an on-premises Power BI Report Server and query its reports, datasets, and PBIX models","icon":"server","href":"/data-sources/connectors/power-bi-report-server"},{"title":"Microsoft Fabric","icon":"microsoft","href":"/data-sources/connectors/microsoft-fabric"},{"title":"Microsoft Analysis Services","icon":"boxes","href":"/data-sources/connectors/analysis-services"},{"title":"Tableau","icon":"chart-column","href":"/data-sources/connectors/tableau"},{"title":"Qlik Sense","icon":"activity","href":"/data-sources/connectors/qlik-sense"},{"title":"Qlik Sense on-prem","icon":"activity","href":"/data-sources/connectors/qlik-sense-onprem"},{"title":"Sisense","icon":"boxes","href":"/data-sources/connectors/sisense"},{"title":"Oracle BI","icon":"database","href":"/data-sources/connectors/oracle-bi"},{"title":"Infor OLAP","icon":"server","href":"/data-sources/connectors/infor-olap"},{"title":"Timbr AI","icon":"table","href":"/data-sources/connectors/timbr"},{"title":"Observability and monitoring","icon":"activity","href":"/data-sources/connectors/observability"},{"title":"Kubernetes","icon":"server","href":"/data-sources/connectors/kubernetes"},{"title":"NetApp ONTAP","icon":"hard-drive","href":"/data-sources/connectors/netapp-ontap"},{"title":"Brocade Fabric OS","icon":"network","href":"/data-sources/connectors/brocade"},{"title":"VMware Aria Operations","icon":"boxes","href":"/data-sources/connectors/aria-operations"},{"title":"Business applications","icon":"boxes","href":"/data-sources/connectors/business-apps"},{"title":"Files and Directories","icon":"folder","href":"/data-sources/connectors/files-and-directories"},{"title":"Amazon S3","icon":"aws","href":"/data-sources/connectors/s3"},{"title":"SharePoint and OneDrive","icon":"microsoft","href":"/data-sources/connectors/sharepoint-onedrive"},{"title":"SharePoint Server (on-prem)","icon":"microsoft","href":"/data-sources/connectors/sharepoint-server"},{"title":"OpenText Documentum","icon":"folder-open","href":"/data-sources/connectors/documentum"},{"title":"OneNote","icon":"microsoft","href":"/data-sources/connectors/onenote"},{"title":"SharePoint Lists","icon":"microsoft","href":"/data-sources/connectors/sharepoint-lists"},{"title":"Google Drive","icon":"google","href":"/data-sources/connectors/google-drive"},{"title":"Gmail and Outlook Mail","icon":"mail","href":"/data-sources/connectors/mail"},{"title":"CSV, QVD and PBIX","icon":"table","href":"/data-sources/connectors/file-formats"},{"title":"Browser","icon":"globe","href":"/data-sources/connectors/browser"}]},{"title":"MCP Connections","icon":"plug","href":"/data-sources/mcp"},{"title":"Custom API Connections","icon":"webhook","href":"/data-sources/custom-api"},{"title":"User Context Forwarding","icon":"user-check","href":"/data-sources/user-context"},{"title":"Dynamic Params","icon":"brackets-curly","href":"/data-sources/dynamic-params"},{"group":"Custom Queries and Row-Level Security","expanded":false,"pages":[{"title":"Overview","icon":"zap","href":"/bow-fast/overview"},{"title":"Custom queries","icon":"database","href":"/bow-fast/custom-queries"},{"title":"Row-level security","icon":"shield-check","href":"/bow-fast/row-level-security"},{"title":"Cut ClickHouse Cloud costs","sidebarTitle":"ClickHouse Cloud costs","icon":"moon","href":"/bow-fast/clickhouse-cloud-costs"}]}]},{"group":"Models","pages":[{"title":"Models and LLM providers","href":"/using-bow/llm"}]},{"group":"Observe and Govern","pages":[{"title":"Observe and govern","icon":"activity","href":"/observe-and-govern/overview"},{"title":"Runs, traces, and diagnostics","icon":"git-branch","href":"/observe-and-govern/runs-and-traces"},{"title":"Usage, cost, and quality","icon":"chart-no-axes-combined","href":"/observe-and-govern/usage-cost-quality"},{"title":"Quotas and usage limits","icon":"gauge","href":"/observe-and-govern/quotas-and-usage-limits"},{"title":"Audit logs","icon":"scroll","href":"/observe-and-govern/audit-logs"}]},{"group":"Channels","pages":[{"title":"Channels Overview","icon":"messages-square","href":"/channels/overview"},{"title":"Slack","icon":"slack","href":"/channels/slack"},{"title":"Microsoft Teams","icon":"microsoft","href":"/channels/teams"},{"title":"Google Chat","icon":"google","href":"/channels/google-chat"},{"title":"WhatsApp","icon":"whatsapp","href":"/channels/whatsapp"},{"title":"Email","icon":"mail","href":"/channels/email"},{"title":"OAuth Apps","sidebarTitle":"OAuth Apps","icon":"key-round","href":"/channels/oauth-apps"}]},{"group":"Open Source and Enterprise","pages":[{"title":"Open source and Enterprise","icon":"building-2","href":"/enterprise/overview"},{"title":"License Key","icon":"key-round","href":"/enterprise/license"}]}]},"title":"Power BI - Bag of words","buildId":"dpl_HcHmUS6i3UwswnTHJUEdcTtwfdTQ","deploymentId":"dpl_HcHmUS6i3UwswnTHJUEdcTtwfdTQ","dynamics":[],"chunks":[]}