
Picture this: your data engineering team has just finished refactoring the enterprise sales data model. They've renamed a key measure, restructured a few tables, and optimized the DAX. It's a clean improvement — except that three hours after the deployment, your inbox is flooded. Reports across seven workspaces are broken. The regional sales dashboard that executives use in their Monday morning briefing is showing blank visuals. The automated KPI alert that triggers the supply chain team's reorder process is returning errors. Nobody knew those reports depended on that dataset, because nobody had a map of the dependencies.
This is the central problem that Power BI's lineage and impact analysis features exist to solve. At its core, lineage tracking answers the question: where does this data come from, and what does it feed? Impact analysis flips that question around: if I change this thing, what else breaks? Together, they give you the situational awareness that separates a professionally managed Power BI estate from a collection of reports that nobody dares to touch.
By the end of this lesson, you'll have a working methodology for auditing your Power BI workspace dependencies, using both the built-in lineage view and the Power BI REST API to extract and analyze dependency data programmatically. You'll be able to answer questions like "which reports will break if I retire this dataset?" before you make changes, not after.
What you'll learn:
You should already be comfortable with:
Before you start clicking through the Power BI service, it helps to understand what the lineage graph is actually modeling. Power BI tracks three categories of upstream dependencies and one category of downstream consumers.
Upstream (data sources flowing in):
Downstream (consumers):
The lineage view in the Power BI service renders this as a directed acyclic graph (DAG), left to right: sources on the left, reports and dashboards on the right. When you're reading this graph as a change management tool, you start from any node in the middle and ask two questions: "What feeds me?" (left) and "What depends on me?" (right).
One critical nuance: cross-workspace dependencies are real and common in well-structured Power BI estates. A "shared dataset" pattern — where a single certified dataset in a central workspace powers reports in many department workspaces — is exactly the scenario where impact analysis matters most. The lineage view spans workspace boundaries, which is what makes it genuinely useful for enterprise environments.
To access the lineage view, navigate to any workspace in the Power BI service and look for the view switcher in the top-right area of the workspace content list. You'll see icons for List view, Grid view, and Lineage view. Switch to Lineage view.
The first thing most practitioners notice is that the canvas gets busy quickly. A real enterprise workspace with 20 datasets, 15 dataflows, and 50 reports produces a graph that's initially overwhelming. Here's how to read it systematically.
Zoom and focus first. Use Ctrl+Scroll to zoom out until you can see the overall shape of the graph. You're looking for the structure: how many distinct lineage chains exist? Are there datasets that appear to be shared hubs (many reports hanging off a single dataset)? Are there isolated clusters that have no connection to any data source (orphaned artifacts)?
Click a node to highlight its lineage. When you click any artifact — say, a dataset called Sales Performance - Certified — the graph dims everything unrelated and highlights only the direct chain: the data source or dataflow feeding that dataset, and all the reports consuming it. This is your immediate impact scope.
The "Impact analysis" button is where the real value lives. With any dataset or dataflow node selected, you'll see an "Impact analysis" button appear (it looks like a small icon with radiating lines). Click it. This opens a side panel that lists every downstream artifact that depends on the selected item, organized by workspace. The panel shows you:
That "Notify users" feature sounds minor but matters enormously in practice. When you're about to push a breaking change, you can use this panel to send a pre-emptive notification to every report owner in the dependency chain — directly from the impact analysis panel, without needing to look up anyone's email address manually.
Tip: The impact analysis side panel only shows artifacts within your organization's Power BI tenant. If your dataset is consumed via the "Publish to web" feature or embedded in an external application, those consumers won't appear here. Always confirm with your app development team whether a dataset is used in any embedded scenarios.
Tracing cross-workspace lineage. When a report in Workspace B is built on a dataset from Workspace A, the lineage view in Workspace A will show that report as a downstream consumer. The lineage view in Workspace B will show the shared dataset from Workspace A as an upstream node, with a small workspace badge indicating it lives in a different workspace. This is the clearest visual indicator that you have a cross-workspace dependency.
The built-in lineage view is excellent for ad-hoc exploration, but it doesn't scale to enterprise governance. If you manage an estate with 50+ workspaces, you need a programmatic approach: extract the dependency metadata, store it, query it, and surface it in a governance report.
The Power BI Admin APIs give you everything you need. The two most important endpoints for lineage work are:
GET /admin/workspaces/getInfo (with lineage=true) — Returns detailed metadata about workspaces including their artifacts and the relationships between them.GET /admin/datasets/{datasetId}/upstreamDataflows — Returns the upstream dataflow dependencies for a specific dataset.Let's build this step by step using Power Query in Power BI Desktop, which is the most practical approach for practitioners who want to build a governance dashboard without writing a full application.
You need a service principal or a delegated user account with Power BI Admin rights. For a governance solution, a service principal is strongly preferred because it doesn't break when an employee leaves.
Register an Azure App Registration in the Azure Portal, grant it the Tenant.Read.All Power BI permission (application permission, not delegated), and grant admin consent. Note your client_id, client_secret, and tenant_id.
In Power BI Desktop, create a new blank query and use this M code to fetch an access token:
let
TenantId = "your-tenant-id-here",
ClientId = "your-client-id-here",
ClientSecret = "your-client-secret-here",
TokenUrl = "https://login.microsoftonline.com/" & TenantId & "/oauth2/v2.0/token",
TokenBody = "grant_type=client_credentials"
& "&client_id=" & ClientId
& "&client_secret=" & ClientSecret
& "&scope=https://analysis.windows.net/powerbi/api/.default",
TokenResponse = Json.Document(
Web.Contents(
TokenUrl,
[
Headers = [#"Content-Type" = "application/x-www-form-urlencoded"],
Content = Text.ToBinary(TokenBody)
]
)
),
AccessToken = TokenResponse[access_token]
in
AccessToken
Name this query fnGetAccessToken. You'll reference it in subsequent queries.
Warning: Never hardcode secrets directly in a
.pbixfile that will be shared or committed to version control. In production, store credentials in Azure Key Vault and use a parameterized approach, or use Power BI's built-in credential management in the dataset settings after publishing.
The Admin Workspace Scan API is a two-step process: you trigger a scan, wait for it to complete, and then retrieve the results. Here's a complete Power Query function that handles this workflow:
let
AccessToken = fnGetAccessToken,
BaseUrl = "https://api.powerbi.com/v1.0/myorg/admin/",
AuthHeader = [Authorization = "Bearer " & AccessToken],
// Step 1: Trigger the workspace scan
ScanRequest = Json.Document(
Web.Contents(
BaseUrl & "workspaces/getInfo?lineage=true&datasourceDetails=true&datasetSchema=true&datasetExpressions=true",
[
Headers = AuthHeader & [#"Content-Type" = "application/json"],
Content = Json.FromValue([workspaces = {}]) // Empty = all workspaces
]
)
),
ScanId = ScanRequest[id],
// Step 2: Poll until the scan completes
// In practice, add a Function.InvokeAfter or manual retry logic
ScanStatus = Json.Document(
Web.Contents(
BaseUrl & "workspaces/scanStatus/" & ScanId,
[Headers = AuthHeader]
)
),
// Step 3: Retrieve scan results
ScanResult = Json.Document(
Web.Contents(
BaseUrl & "workspaces/scanResult/" & ScanId,
[Headers = AuthHeader]
)
)
in
ScanResult
Tip: The scan API can take 30–60 seconds to complete for large tenants. Power Query's refresh will time out if you poll in a tight loop. A pragmatic workaround: run the trigger query once manually to get a scan ID, then hardcode that scan ID into a separate "fetch results" query during development. For production, use a Power Automate flow or Azure Function to orchestrate the scan-poll-retrieve cycle and store results in Azure SQL or a Fabric lakehouse.
The scan result JSON is deeply nested. Here's how to extract it into three useful flat tables: Workspaces, Datasets, and Reports — with their relationship keys preserved.
// Query: WorkspacesTable
let
RawScan = WorkspaceScanResult, // reference your scan result query
WorkspacesList = RawScan[workspaces],
WorkspacesTable = Table.FromList(WorkspacesList, Splitter.SplitByNothing()),
Expanded = Table.ExpandRecordColumn(
WorkspacesTable, "Column1",
{"id", "name", "type", "state", "isOnDedicatedCapacity", "capacityId"}
)
in
Expanded
// Query: DatasetsTable
let
RawScan = WorkspaceScanResult,
WorkspacesList = RawScan[workspaces],
WorkspacesTable = Table.FromList(WorkspacesList, Splitter.SplitByNothing()),
// Add workspace ID to each row before expanding
WithWorkspaceId = Table.AddColumn(
WorkspacesTable,
"workspaceId",
each [Column1][id]
),
WithWorkspaceName = Table.AddColumn(
WithWorkspaceId,
"workspaceName",
each [Column1][name]
),
// Expand datasets column
WithDatasets = Table.AddColumn(
WithWorkspaceName,
"datasets",
each try [Column1][datasets] otherwise {}
),
Expanded = Table.ExpandListColumn(WithDatasets, "datasets"),
FilteredNulls = Table.SelectRows(Expanded, each [datasets] <> null),
ExpandedDatasets = Table.ExpandRecordColumn(
FilteredNulls,
"datasets",
{"id", "name", "configuredBy", "endorsementDetails", "upstreamDataflows", "datasourceUsages"}
),
// Rename for clarity
Renamed = Table.RenameColumns(
ExpandedDatasets,
{{"id", "datasetId"}, {"name", "datasetName"}}
),
// Select only what we need
Final = Table.SelectColumns(
Renamed,
{"workspaceId", "workspaceName", "datasetId", "datasetName",
"configuredBy", "endorsementDetails", "upstreamDataflows", "datasourceUsages"}
)
in
Final
// Query: ReportsTable
let
RawScan = WorkspaceScanResult,
WorkspacesList = RawScan[workspaces],
WorkspacesTable = Table.FromList(WorkspacesList, Splitter.SplitByNothing()),
WithWorkspaceId = Table.AddColumn(WorkspacesTable, "workspaceId", each [Column1][id]),
WithWorkspaceName = Table.AddColumn(WithWorkspaceId, "workspaceName", each [Column1][name]),
WithReports = Table.AddColumn(
WithWorkspaceName,
"reports",
each try [Column1][reports] otherwise {}
),
Expanded = Table.ExpandListColumn(WithReports, "reports"),
FilteredNulls = Table.SelectRows(Expanded, each [reports] <> null),
ExpandedReports = Table.ExpandRecordColumn(
FilteredNulls,
"reports",
{"id", "name", "datasetId", "createdBy", "modifiedBy", "modifiedDateTime"}
),
Renamed = Table.RenameColumns(
ExpandedReports,
{{"id", "reportId"}, {"name", "reportName"}}
),
Final = Table.SelectColumns(
Renamed,
{"workspaceId", "workspaceName", "reportId", "reportName",
"datasetId", "createdBy", "modifiedBy", "modifiedDateTime"}
)
in
Final
With these three tables, you can build a data model in Power BI Desktop. The DatasetsTable[datasetId] relates to ReportsTable[datasetId], giving you a one-to-many relationship between datasets and the reports that depend on them. When you slice by a dataset, you immediately see all dependent reports across all workspaces.
Now you have the raw data. Let's structure it into something your team can actually use for change management decisions.
In Power BI Desktop's Model view, establish these relationships:
DatasetsTable[datasetId] → ReportsTable[datasetId] (one-to-many)WorkspacesTable[workspaceId] → DatasetsTable[workspaceId] (one-to-many)Add a separate relationship from WorkspacesTable to ReportsTable via the report's workspace ID (note: this is a different column from the dataset's workspace ID — a report in Workspace B can depend on a dataset in Workspace A, so the workspace IDs in the Reports table refer to where the report lives, not where its dataset lives).
// How many reports depend on a selected dataset?
Reports Using Dataset =
COUNTROWS(RELATEDTABLE(ReportsTable))
// How many cross-workspace dependencies exist?
Cross-Workspace Dependencies =
COUNTROWS(
FILTER(
ReportsTable,
ReportsTable[workspaceId] <> RELATED(DatasetsTable[workspaceId])
)
)
// Identify datasets with no downstream reports (orphan candidates)
Datasets With No Reports =
CALCULATE(
COUNTROWS(DatasetsTable),
FILTER(
DatasetsTable,
CALCULATE(COUNTROWS(ReportsTable)) = 0
)
)
// Days since last report modification (staleness indicator)
Days Since Last Modified =
DATEDIFF(
MAX(ReportsTable[modifiedDateTime]),
TODAY(),
DAY
)
Page 1: Dataset Impact Explorer A slicer on dataset name and workspace, a card showing "Reports Using Dataset," a table showing all dependent reports with their workspace, owner, and last modified date, and a conditional formatting rule that highlights cross-workspace dependencies in amber.
Page 2: Orphaned Assets A filtered table showing all datasets with zero downstream reports, datasets whose only downstream reports haven't been modified in more than 180 days, and dataflows with no downstream datasets. This page answers the question "what can we safely retire?"
Page 3: Dependency Heat Map A matrix with workspaces on rows and datasets on columns, with the cell value being the count of reports in that workspace consuming that dataset. This gives leadership a visual of which datasets carry the highest enterprise-wide risk if they change.
Page 4: Change Risk Assessment
A ranked table of datasets sorted by Reports Using Dataset descending, filtered to only Certified or Promoted datasets (the ones most likely to be shared broadly). This is your change management prioritization list — the datasets at the top require the most careful change management process.
Having a governance report is only useful if it's embedded in your actual deployment workflow. Here's a practical process you can implement without heavy tooling.
Before any dataset schema change (renaming tables, removing columns, changing measure logic, modifying refresh schedules), the dataset owner must:
Run the Impact Analysis — Open the lineage view for the dataset, use the Impact Analysis panel to see all downstream consumers. Cross-reference with the governance report for user count data. Document the findings.
Classify the change risk — Use this simple rubric:
Notify downstream owners — For Medium risk, use the "Notify users" button in the Impact Analysis panel. For High risk, require a synchronous meeting with downstream report owners before proceeding.
Stage the change — For High risk changes, deploy to a Development workspace first. Publish a parallel "staging" version of the dataset, update one test report to point at it, and validate the output matches expectations before cutting over the production dataset.
After a dataset change deploys to production:
Warning: Power BI does not automatically notify report consumers when a dataset schema change breaks their visuals. Broken visuals typically show a generic "Couldn't load the visual" error without any indication of why. This is why proactive communication from your change process is non-negotiable.
For teams managing large estates, you can build a Power Automate flow that:
configuredBy email addresses from the reports and datasets in the dependency chain.This turns a manual notification step into an automatic one, and creates an audit trail of every change notification sent.
This exercise walks you through a complete lineage audit of a realistic Power BI estate. If you're following along with your own tenant, substitute your actual workspace and dataset names.
Scenario: You've been asked to audit the dependency graph for your organization's Finance - Certified Metrics dataset before the finance team performs a year-end restructuring of the data model. You need to produce a report that lists all dependent artifacts, their owners, and a risk classification.
Step 1: Visual Lineage Exploration (15 minutes)
Open the workspace where Finance - Certified Metrics lives. Switch to Lineage view. Click the dataset node and note: how many reports are directly connected? Are any of them in different workspaces? Are there any other datasets downstream (indicating composite model usage)?
Click "Impact analysis" and screenshot the panel for your documentation. Note the user count figures — this tells you the business-criticality tier.
Step 2: API Extraction (30 minutes)
Using the Power Query templates from earlier in this lesson, set up a new Power BI Desktop file with the three flat tables: Workspaces, Datasets, and Reports. Build the data model relationships.
Filter DatasetsTable to find the row for Finance - Certified Metrics. Note its datasetId — you'll use this as your reference key.
Filter ReportsTable to only rows where datasetId matches. How many reports did you find? Are there any rows where the report's workspaceId differs from the dataset's workspaceId? Those are your cross-workspace dependencies.
Step 3: Risk Classification (20 minutes)
Using the filtered reports list, build a table with these columns:
| Report Name | Workspace | Owner | Last Modified | Endorsed | Risk Factor |
|---|---|---|---|---|---|
| (from API) | (from API) | (from API) | (from API) | (from API) | (your assessment) |
Assign each report a Risk Factor: Low, Medium, or High, using the rubric from the previous section. A report owned by a different team or in a different workspace automatically gets at least Medium.
Step 4: Generate the Stakeholder Summary (15 minutes)
Write a one-paragraph impact summary that a non-technical stakeholder (the finance data steward, for example) could read and act on. It should cover:
This summary becomes your change request attachment.
The lineage view only shows artifacts within the Power BI service. If your dataset is consumed by:
Get Data > Power BI)...those consumers will not appear in the lineage view or the API scan results. Always ask your application teams and finance teams (Excel is a major hidden consumer) before treating the lineage view as complete.
A dataset with zero downstream reports in Power BI could still be:
ExecuteQueries REST API by a custom applicationBefore retiring any dataset, query the Power BI activity log (available via the Admin API at GET /admin/activityevents) and filter for QueryDataset and AnalyzeInExcel events against that dataset ID over the past 90 days. If you see activity, the dataset is not truly orphaned.
// Query: ActivityLog - Dataset Access Events
let
AccessToken = fnGetAccessToken,
// Activity log supports one day per call; loop over a date range in practice
StartDate = "2024-11-01T00:00:00",
EndDate = "2024-11-01T23:59:59",
Response = Json.Document(
Web.Contents(
"https://api.powerbi.com/v1.0/myorg/admin/activityevents"
& "?startDateTime='" & StartDate & "'"
& "&endDateTime='" & EndDate & "'"
& "&$filter=Activity eq 'QueryDataset'",
[Headers = [Authorization = "Bearer " & AccessToken]]
)
),
Events = Response[activityEventEntities],
EventsTable = Table.FromList(Events, Splitter.SplitByNothing()),
Expanded = Table.ExpandRecordColumn(
EventsTable, "Column1",
{"Id", "CreationTime", "UserId", "ArtifactId", "ArtifactName", "Activity", "WorkspaceName"}
)
in
Expanded
Practitioners often focus on dataset-to-report dependencies and forget that dataflows introduce another dependency layer. If you modify a dataflow (rename an output table, remove a column, change a type), every dataset that reads from that dataflow will be affected — but the lineage view may not make this as visually obvious as the dataset-to-report relationship.
Always check upstreamDataflows in the scan results for any dataset you're working with. A dataset that shows "no sensitive downstream reports" may still be the only consumer of a dataflow that 12 other datasets share.
The Admin Workspace Scan API has a limit of one concurrent scan per tenant. If your governance refresh triggers a scan while another Admin is running a scan, yours will queue. Also, large tenants (thousands of workspaces) can produce scan result JSON files measured in hundreds of megabytes. Power Query will struggle to parse these in-memory. For tenants over ~200 workspaces, route the API results through Azure Data Factory, Fabric Pipelines, or a simple Azure Function that writes the JSON to a lakehouse, then query it from there.
The lineage metadata in the Power BI service is not always instantly updated. There can be a lag of several minutes to a few hours between when you create a new report on a dataset and when that relationship appears in the lineage view. For change management purposes, always scan 24 hours before a planned change, not immediately before it.
You've now built a complete methodology for managing dataset dependencies in a Power BI enterprise environment. The core insight to take away is that lineage analysis is not a one-time audit — it's an ongoing governance practice that needs to be embedded into your change management process, your deployment workflow, and your data catalog strategy.
To recap what you've implemented:
Where to go from here:
Extend to Microsoft Purview. If your organization uses Microsoft Purview (formerly Azure Purview), Power BI lineage integrates directly into the Purview data catalog, giving you cross-platform lineage that spans Azure Data Factory, Azure SQL, Synapse, and Power BI in a single graph. This is the enterprise-grade evolution of what you've built here.
Automate the governance report refresh. Schedule your governance Power BI dataset to refresh daily using a pipeline that orchestrates the API scan, stores results in a lakehouse or SQL database, and refreshes the dataset automatically. This gives you a fresh lineage snapshot every morning.
Build a dataset certification workflow. Use the governance data you've built to create a formal certification process: before a dataset can be marked "Certified" in Power BI, it must have a documented lineage entry in your catalog, a designated data owner, and a completed impact analysis on file.
Explore XMLA-based lineage tools. Tools like pbi-tools and Tabular Editor can extract model metadata including table references and measure dependencies at the DAX level — a layer deeper than what the Admin API exposes. Combining XMLA-level dependency tracking with workspace-level lineage gives you end-to-end visibility from raw SQL column all the way to the DAX measure in a dashboard tile.
Lineage is ultimately about trust. When your organization's data consumers know that changes are tracked, communicated, and managed with rigor, they can rely on the data — and that's the foundation everything else is built on.
Learning Path: Enterprise Power BI