metadata-ingestion/docs/sources/powerbi/powerbi_post.md
Use the Important Capabilities table above as the source of truth for supported features and whether additional configuration is required.
PowerBI Source supports two modes for handling user ownership:
When ownership.create_corp_user: false (default), PowerBI will:
This is the recommended approach as it prevents PowerBI from overwriting user profiles from your identity provider.
ownership:
create_corp_user: false # Default - soft references only
When ownership.create_corp_user: true, PowerBI will:
displayName and email from PowerBIWarning: Only use this if PowerBI is your authoritative source for user information.
ownership:
create_corp_user: true # Opt-in - creates user entities
You can limit which users become owners using owner_criteria. Only users with at least one of the specified access rights will be assigned as owners:
ownership:
owner_criteria:
- ReadWriteReshareExplore
- Owner
- Admin
Valid values depend on the PowerBI access right types for your resources (e.g., dataset, report, dashboard). If owner_criteria is not set or is an empty list, all users with principalType: User qualify as owners.
PowerBI ingestion uses two metadata paths per workspace:
| Path | When | What it fetches | API volume |
|---|---|---|---|
| Admin scan | The configured principal has admin-API access and the workspace scan returns data. | Reports, users, datasets, lineage, endorsements — all from a single workspace scan response. | 1 scan request per workspace batch |
| Workspace listing fallback | The admin scan returns no data for a workspace (permissions, throttling, etc.). | Reports via the per-workspace /reports endpoint; users via /admin/reports/{id}/users per report (only when extract_ownership: true). | O(workspaces + reports) requests |
When the fallback engages, the ingestion report includes a Report Scan Fallback Active entry per workspace so you can correlate that workspace's slower ingestion with the missing scan output.
This source extracts table lineage for tables present in PowerBI Datasets. Lets consider a PowerBI Dataset SALES_REPORT and a PostgreSQL database is configured as data-source in SALES_REPORT dataset.
Consider SALES_REPORT PowerBI Dataset has a table SALES_ANALYSIS which is backed by SALES_ANALYSIS_VIEW of PostgreSQL Database then in this case SALES_ANALYSIS_VIEW will appear as upstream dataset for SALES_ANALYSIS table.
You can control table lineage ingestion using extract_lineage configuration parameter, by default it is set to true.
PowerBI Source extracts the lineage information by parsing PowerBI M-Query expressions and from dataset data returned by the PowerBI API.
The source will attempt to extract information from ODBC connection strings in M-Query expressions to determine the database type. If the database type matches a supported platform and the source is able to extract enough information to construct a valid Dataset URN, it will extract lineage for that data source.
PowerBI Source will extract lineage for the below listed PowerBI Data Sources:
Native SQL query parsing is supported for Snowflake, Amazon Redshift, Oracle, and ODBC data sources.
PowerBI users can connect to Oracle via a TNS alias (any tnsnames.ora-based deployment) and embed SQL inline using a Query= argument:
let
Source = Oracle.Database(
"EDWPSFN",
[HierarchicalNavigation = true,
Query = "SELECT … FROM PS_VENDOR, PS_COR_CNTRCT_PROJ …"])
in
Source
Three Oracle.Database first-argument forms are recognized:
host:port/service[.domain]EDWPSFN, MYDB.WORLD(DESCRIPTION=(ADDRESS=…)(CONNECT_DATA=(SERVICE_NAME=foo)))For the bare-alias and descriptor forms, the alias / SERVICE_NAME becomes the server key for server_to_platform_instance lookup. The lookup is case-insensitive, so EDWPSFN in the M-Query matches an EDWPSFN (or edwpsfn) entry in the recipe.
The generated upstream URN must match the URN that your Oracle ingestion produces for the same table, otherwise the lineage edge points at a dataset that does not exist. Oracle ingestion emits 2-part schema.table URNs by default, and 3-part database.schema.table URNs only when it runs with add_database_name_to_urn: true.
PowerBI derives the database segment from the connection form:
| Oracle.Database form | Database segment |
|---|---|
EZ-Connect host:port/service | the service (always 3-part) |
| Bare TNS alias / descriptor | none — 2-part by default |
So a bare TNS alias produces a 2-part URN, which is correct for a default Oracle ingestion. If your Oracle ingestion uses add_database_name_to_urn: true, set default_database on the alias entry to supply the missing segment (use the same value as the Oracle ingestion's database / urn_db_name):
source:
type: powerbi
config:
server_to_platform_instance:
EDWPSFN:
default_database: edwprd # only if Oracle ingestion uses 3-part URNs
default_schema: sysadm # owner schema for unqualified inline-SQL tables
# platform_instance: EDWPSFN # only set this if your Oracle ingestion uses one
default_schema)Because inline Query= SQL often references unqualified tables, declare a default_schema on the alias entry so those references resolve to the ingested Oracle datasets. default_schema only applies to inline native SQL — hierarchical navigation takes the schema from the M-Query itself.
If default_schema is missing and the inline SQL references unqualified tables, lineage will still be drawn for any qualified tables in the SQL, and a structured warning will appear in the ingestion report telling you exactly which alias needs the knob set.
At least one of default_schema / default_database must be set for an Oracle entry; a mapping that needs neither should be a plain platform_instance / env entry.
When using Amazon Athena via ODBC that queries federated data sources (e.g., Athena querying MySQL or PostgreSQL via federated connectors), the lineage URNs will default to the Athena platform. Use athena_table_platform_override to point lineage to the actual source platform instead of Athena.
Configuration:
source:
type: powerbi
config:
# ... other config ...
dsn_to_platform_name:
MyAthenaDSN: athena
athena_table_platform_override:
# DSN-scoped key (takes precedence)
"MyAthenaDSN:analytics.users": mysql
# Global key (fallback for any DSN)
"reporting.orders": postgres
Key format:
"DSN_NAME:database.table" - applies only to specific DSN"database.table" - applies to all DSNsDSN-scoped keys take precedence over global keys, allowing different overrides for the same table name across different Athena data sources.
Note: This override only applies to Athena ODBC connections. For other ODBC platforms, lineage will use the platform determined from the DSN configuration.
For example, consider the SQL query shown below. The table OPERATIONS_ANALYTICS.TRANSFORMED_PROD.V_UNIT_TARGET will be ingested as an upstream table.
let
Source = Value.NativeQuery(
Snowflake.Databases(
"sdfsd788.ws-east-2.fakecomputing.com",
"operations_analytics_prod",
[Role = "OPERATIONS_ANALYTICS_MEMBER"]
){[Name = "OPERATIONS_ANALYTICS"]}[Data],
"select #(lf)UPPER(REPLACE(AGENT_NAME,\'-\',\'\')) AS Agent,#(lf)TIER,#(lf)UPPER(MANAGER),#(lf)TEAM_TYPE,#(lf)DATE_TARGET,#(lf)MONTHID,#(lf)TARGET_TEAM,#(lf)SELLER_EMAIL,#(lf)concat((UPPER(REPLACE(AGENT_NAME,\'-\',\'\'))), MONTHID) as AGENT_KEY,#(lf)UNIT_TARGET AS SME_Quota,#(lf)AMV_TARGET AS Revenue_Quota,#(lf)SERVICE_QUOTA,#(lf)BL_TARGET,#(lf)SOFTWARE_QUOTA as Software_Quota#(lf)#(lf)from OPERATIONS_ANALYTICS.TRANSFORMED_PROD.V_UNIT_TARGETS#(lf)#(lf)where YEAR_TARGET >= 2020#(lf)and TEAM_TYPE = \'foo\'#(lf)and TARGET_TEAM = \'bar\'",
null,
[EnableFolding = true]
),
#"Added Conditional Column" = Table.AddColumn(
Source,
"Has PS Software Quota?",
each
if [TIER] = "Expansion (Medium)" then
"Yes"
else if [TIER] = "Acquisition" then
"Yes"
else
"No"
)
in
#"Added Conditional Column"
Use full-table-name in from clause. For example dev.public.category
Lets consider a M-Query which combine two PostgreSQL tables. Such M-Query can be written as per below patterns.
Pattern-1
let
Source = PostgreSQL.Database("localhost", "book_store"),
book_date = Source{[Schema="public",Item="book"]}[Data],
issue_history = Source{[Schema="public",Item="issue_history"]}[Data],
combine_result = Table.Combine({book_date, issue_history})
in
combine_result
Pattern-2
let
Source = PostgreSQL.Database("localhost", "book_store"),
combine_result = Table.Combine({Source{[Schema="public",Item="book"]}[Data], Source{[Schema="public",Item="issue_history"]}[Data]})
in
combine_result
Pattern-2 is not supported for upstream table lineage extraction as it uses nested item-selector i.e. {Source{[Schema="public",Item="book"]}[Data], Source{[Schema="public",Item="issue_history"]}[Data]} as argument to M-QUery table function i.e. Table.Combine
Pattern-1 is supported as it first assigns the table from schema to variable and then variable is used in M-Query Table function i.e. Table.Combine
By default, extracting endorsement information to tags is disabled. The feature may be useful if organization uses endorsements to identify content quality.
Please note that the default implementation overwrites tags for the ingested entities, if you need to preserve existing tags, consider using a transformer with semantics: PATCH tags instead of OVERWRITE.
The profiling implementation is done through querying DAX query endpoint. Therefore, the principal needs to have permission to query the datasets to be profiled. Usually this means that the service principal should have Contributor role for the workspace to be ingested. Profiling is done with column-based queries to be able to handle wide datasets without timeouts.
Take into account that the profiling implementation executes a fairly big number of DAX queries, and for big datasets this is a significant load to the PowerBI system.
The profiling_pattern setting may be used to limit profiling actions to only a certain set of resources in PowerBI. Both allowed and deny rules are matched against the following pattern for every table in a PowerBI Dataset: workspace_name.dataset_name.table_name. Users may limit profiling with these settings at table level, dataset level or workspace level.
tenant_id, client_id, and client_secret, and confirm the app has the required Power BI API permissions.