Custom Visuals Inventory for Fabric

Custom visuals are great — until they break, get deprecated, or you need to know which reports actually depend on them. I built a small Fabric notebook that gives you a tenant-wide answer in seconds.

Big thanks to Steinick Sibarani for the nudge that pushed me to put this together quickly — the conversation around custom-visual governance in his community thread was exactly the spark this needed.

The Microsoft community thread by Steinick Sibarani that triggered this notebook

What it does

Custom Visuals Inventory uses semantic-link-labs to list every custom visual used across one workspace, a list of workspaces, or your entire tenant. You get:

  • A flat table: workspace, report, visual name, visual GUID
  • Top visuals by report count (where is TRUECHART actually used?)
  • Heaviest reports by custom-visual count
  • A PBIR-format inventory (PBIR vs. PBIRLegacy) — because only PBIR reports can be scanned

One MODE switch flips between current, list, and all workspaces. That’s it.

Notebook on GitHub: PBI-Tools / Notebook Gallery · also published in the Fabric Community Notebook Gallery.

Why this matters for FUAM

FUAM is the go-to solution for tenant-wide Fabric monitoring — workspaces, capacities, activities, refreshables, git connections, tenant settings. But there is one thing it does not ship today: a custom visuals inventory per report.

This notebook fills the gap as a standalone companion — same scope (tenant-wide), same stack (Fabric notebook + Direct Lake friendly output).

If you run a Fabric tenant with more than a handful of reports, this is one notebook you want in your back pocket the next time a custom visual gets deprecated.

Unlimited Power BI Usage History — Snapshot the Built-In Usage Metrics Model to Lakehouse

Power BI’s built-in usage metrics report is great — until you need history beyond 30 days. The underlying dataset is a rolling window: yesterday’s view counts are there, last quarter’s are gone. If you want to track adoption trends, prove ROI, or spot reports that have quietly died, you need to capture that data yourself.

The fix is surprisingly simple: a single Fabric notebook that queries the hidden Report Usage Metrics Model semantic model over XMLA and appends each daily snapshot to Lakehouse Delta tables. Unlimited history, append-only, ~30 lines of Python.

Usage Metrics Snapshot architecture

Honest Disclaimer — Prefer FUAM

Before you build this: if you have any meaningful scale of Power BI / Fabric tenant, use FUAM (Fabric Unified Admin Monitoring) instead. FUAM is the Microsoft-published, open-source accelerator that snapshots not just usage metrics but capacity events, activity logs, refresh history, inventories — the whole tenant story — into a Lakehouse with a ready-made semantic model and report. It is far more complete than what’s in this post.

So when do I reach for the little notebook below instead?

  • A single workspace where you just need report-level view history and don’t want to deploy a full monitoring solution.
  • You need a quick fix today and FUAM rollout is weeks away.
  • You want to understand how the underlying mechanism works — the notebook is small enough to read end-to-end in five minutes.

For everything else: start with FUAM.

The Problem: 30 Days Is Not Enough

  • Rolling window. Each day, the oldest day falls off. Anything older than 30 days is permanently gone — you cannot recover it.
  • No YoY, no trends. Did adoption grow over the last year? Which reports are losing users? You literally cannot answer these questions with the built-in model.
  • No ownership. Microsoft controls the model. Schema changes happen without notice. You cannot extend it, share it, or join it to your own dimensions cleanly.

The Solution: Daily Snapshot to Lakehouse

The hidden Report Usage Metrics Model is a normal semantic model — it just happens to be auto-managed by Microsoft. That means you can hit it with XMLA queries like any other model. Using sempy.fabric.evaluate_dax, we pull each source table, stamp a SnapshotUtc column, and append to Delta tables in a Lakehouse:

import sempy.fabric as fabric
from datetime import datetime, timezone

WORKSPACE_ID = "<your-workspace-guid>"
DATASET_NAME = "Report Usage Metrics Model"
TABLES = ["Views", "Reports", "Users", "Dates", "DistributionMethods", "Platforms"]

snap_ts = datetime.now(timezone.utc)

for t in TABLES:
    df = fabric.evaluate_dax(
        dataset=DATASET_NAME,
        workspace=WORKSPACE_ID,
        dax_string=f"EVALUATE '{t}'",
    )
    df.columns = [c.split("[")[-1].rstrip("]") if "[" in c else c for c in df.columns]
    df["SnapshotUtc"] = snap_ts.isoformat()
    (spark.createDataFrame(df).write
        .mode("append")
        .option("mergeSchema", "true")
        .saveAsTable(f"usage_metrics_{t.lower()}"))

That’s the whole engine. Schedule it daily after 04:00 UTC (the source refreshes around 03:00 UTC) and you have a permanent historical record.

What Lands in the Lakehouse

One Delta table per source table, prefixed usage_metrics_*, with an extra SnapshotUtc column. Append-only — every run adds today’s full snapshot.

Source tableLakehouse tableRole
Viewsusage_metrics_viewsFact
Reportsusage_metrics_reportsDimension
Usersusage_metrics_usersDimension
Datesusage_metrics_datesDimension
DistributionMethodsusage_metrics_distributionmethodsDimension
Platformsusage_metrics_platformsDimension

Three Design Decisions Worth Calling Out

  • Append, not merge. Every snapshot is the full 30-day window. Yes, days overlap across snapshots — but storage is cheap and append is bulletproof. Deduplication happens at query time (see below), not at ingest time.
  • mergeSchema=true. Microsoft occasionally adds columns to the usage metrics model. With schema merging on, the next snapshot just absorbs the new columns instead of crashing.
  • Retention with DELETE. Default 400 days (≈13 months, enough for YoY) — but you can set RETENTION_DAYS = None to keep forever. The delete is a normal Delta operation, so time travel still works if you mess up.

Querying Without Double-Counting

Because every snapshot contains the last 30 days, the same activity row appears in up to 30 snapshots. To get clean numbers, take the latest snapshot per natural key:

WITH ranked AS (
    SELECT v.*,
           ROW_NUMBER() OVER (
               PARTITION BY Date, ReportGuid, UserGuid
               ORDER BY SnapshotUtc DESC
           ) AS rn
    FROM usage_metrics_views v
)
SELECT Date,
       ReportGuid,
       COUNT(*)                  AS views,
       COUNT(DISTINCT UserGuid)  AS distinct_users
FROM ranked
WHERE rn = 1
GROUP BY Date, ReportGuid
ORDER BY Date DESC, views DESC

For older history (where a date appears in only one snapshot), the deduplication is a no-op — the row simply passes through. For the most recent 30 days, you always pick the freshest version of each fact.

Prerequisites

  • Workspace on Premium / PPU / Fabric capacity (XMLA endpoint enabled — read is enough).
  • The Report Usage Metrics Model dataset exists in the workspace. It is auto-created the first time anyone clicks More options → View usage metrics report on any report in that workspace.
  • A Lakehouse attached to the notebook (any Lakehouse in the same workspace works).
  • Tenant setting “Usage metrics for content creators” enabled (admin portal).

Try It — Fabric Notebook Gallery

The notebook is also submitted to the Fabric Notebook Gallery. Grab the .ipynb directly from GitHub and import it into any Fabric workspace:

📓 Usage Metrics Snapshot.ipynb — one notebook, six Delta tables, daily schedule. Set WORKSPACE_ID, attach a Lakehouse, schedule after 04:00 UTC. Done.

Take It Further — A Power BI Report on Top

The Lakehouse tables are already the hard part. From here, building a proper Adoption & Usage Power BI report is a short hop:

  • Point a Direct Lake semantic model at the six usage_metrics_* Delta tables — no import, no refresh schedule, fresh on the next snapshot.
  • Wrap the "latest snapshot per natural key" dedup logic in a SQL view in the Lakehouse SQL endpoint so the model stays simple. One view per fact, deduped, ready to consume.
  • Build the obvious pages: Adoption over time (views & distinct users by month, YoY), Top & bottom reports, Dying reports (had >X views last year, <Y this quarter), Platform & distribution mix.
  • Join to your own Reports dim (owner, business domain, criticality) for actionable cuts — e.g. "critical reports with zero views in 30 days".

That’s the whole point of owning the history: once the data lives in your Lakehouse, you can model and visualise it however your stakeholders need — not however Microsoft’s built-in report happens to ship it.

Wrap-Up

One notebook, one daily schedule, six Delta tables — and the 30-day blind spot is gone. You own the history, you can join it to your own data, and you can build proper adoption dashboards on top.

The full notebook (with retention, health-check, and example query cells) is on GitHub: KornAlexander/PBI-Tools → Notebook Gallery / Usage Metrics Snapshot.ipynb.

The Semantic Layer Debate: BI-Tool-Centric vs. Data-Platform-Centric

The Semantic Layer Debate: BI-Tool-Centric vs. Data-Platform-Centric

Why moving your semantic layer to a separate data platform might not be the obvious choice it seems.

Disclaimer: The opinions expressed in this post are my own and do not represent the views of my employer.


The Debate in 30 Seconds

The analytics industry is in a heated argument about where your business metrics should live:

  • Camp A (BI-Tool-Centric): Define your semantic model in your BI tool. It’s where your users already are.
  • Camp B (Data-Platform-Centric): Define your semantic layer in the data platform. Let every downstream tool consume it.

Camp B has been gaining momentum across the industry. This post walks through seven dimensions to help you critically evaluate both approaches before making a strategic decision.


A Note on Terminology

These two terms are often used interchangeably, but they mean different things:

  • Semantic model — A concrete implementation: tables, relationships, measures, hierarchies, and business logic bundled into a queryable model. In Power BI, this is the semantic model (formerly “dataset”). It’s where your KPIs, time intelligence, and row-level security actually live.
  • Semantic layer — The broader architectural concept: an abstraction that maps business terms to underlying data so consumers don’t need to know table structures or write raw SQL. In Power BI, most often the semantic layer sits on top of one or more semantic models, but it can also incorporate other data sources — lakehouses, warehouses, APIs, or real-time streams.

Throughout this post, “semantic model” refers to the BI tool’s implementation, while “semantic layer” refers to the platform-level abstraction.


1. Maturity & Capability

BI-Tool-Centric Data-Platform-Centric
Calculation Power Mature expression languages with decades of investment. Time intelligence, many-to-many, calculation groups, dynamic formatting. Early-stage. Most platform semantic layers support basic aggregations and simple expressions.
Modeling Richness Star schemas, role-playing dimensions, display folders, synonyms, perspectives, hierarchies. Flat or lightly relational. Still catching up on enterprise modeling patterns.
Verdict ✅ Mature, proven at scale ⚠️ Promising but nascent

Key question to ask: “Can your semantic layer handle the 20 most complex measures in our current BI model? Show me.”


2. Performance

BI-Tool-Centric Data-Platform-Centric
Query Speed In-memory engines, aggregation tables, query folding — built for sub-second interactive analysis. Depends on warehouse engine. Adds network hops. Optimized for large-scale transforms, not slice-and-dice exploration.
User Experience Instant filter, drill, cross-highlight. Users expect this. Latency varies. Risk of degraded interactivity that frustrates business users.
Verdict ✅ Purpose-built for interactivity ⚠️ Acceptable for dashboards, risky for exploration

Key question: “When a business user applies three slicer filters on a 50M row dataset through your semantic layer — how fast does it respond in the slowest realistic scenarios, not just on average?”


3. The “Last Mile” Problem

Even if you externalize metric definitions, the BI tool still needs:

  • Conditional formatting rules
  • Visual-level filters and default aggregations
  • Display folders and field organization
  • Synonyms and linguistic metadata (for Q&A / NLP)
  • Report-level measures and layout-specific logic

The platform semantic layer doesn’t eliminate BI-tool-specific work. It just splits your logic across two places.

You still maintain the BI tool. Now you also maintain the platform layer. That’s not simplification — that’s added complexity.


4. Governance

BI-Tool-Centric Data-Platform-Centric
Single Source of Truth Already established in many orgs. Endorsed datasets, data lineage, deployment pipelines. Promises centralization but adds a new governance surface. Now you govern the platform layer AND the BI layer.
Row-Level Security Mature, integrated, tested. Must be re-implemented or passed through — another seam where things break.
Business Ownership Business analysts can see, test, and validate metrics in the tool they use daily. Metrics live in a platform business users can’t access. Ownership shifts to engineering.
Verdict ✅ Governance where users are ⚠️ Governance where engineers are

Key question: “When a business user disputes a number, can they trace the metric definition themselves — or do they need to file a ticket with the data platform team?”


5. The AI Agent Angle

This is the emerging frontier. AI agents need semantic context to reason about data. The question is: which semantic layer feeds them?

BI-Tool-Centric Data-Platform-Centric
Richness for AI BI models already contain relationships, hierarchies, descriptions, business logic — exactly what an agent needs to generate accurate answers. Metric definitions exist, but often lack the relational richness and business context an agent needs.
Proximity to Users Agents that sit on top of the BI model serve the same users who already trust that model. Agents on the platform layer may produce answers that don’t match what users see in their BI reports — eroding trust.
Maintenance One model feeds both reports and agents. Two models: one for agents (platform), one for visuals (BI). Drift risk is real.

Key question: “If my AI agent and my BI report give different answers for the same KPI, which one is wrong — and whose job is it to fix it?”


6. The Lock-In Reality

A common argument against BI-tool semantic models is lock-in. Let’s be honest about what’s really happening:

Claim Reality
“Your BI tool is locking you in!” Moving your semantic layer to any data platform is… also lock-in. You’re choosing which dependency to accept.
“We’re open and interoperable!” Every platform’s semantic layer has proprietary syntax, proprietary APIs, and proprietary tooling. “Open” is a spectrum, not a binary.
“Define once, consume everywhere!” In practice, every consuming tool interprets the layer differently, supports different subsets, and requires tool-specific workarounds.
“This is customer-driven!” The push for platform-centric semantic layers shifts the center of gravity — and revenue — to the data platform. Follow the incentives.

Key question: “If I adopt your semantic layer and want to switch data platforms in 3 years, how portable are my metric definitions?”


7. Who Actually Has This Problem?

The platform-centric approach assumes you need one semantic layer serving many BI tools. But ask yourself:

  • How many BI tools does your organization actually use at scale?
  • Of those, how much genuine metric overlap exists between them?
  • Is the overlap large enough to justify an entirely new architectural layer?

For most enterprises, the honest answer is: we’ve standardized on one BI tool, and the multi-tool problem is theoretical. The platform semantic layer is an elegant solution to a problem you probably don’t have.


The Decision Framework

To make this actionable, here’s a decision tree that walks you through the key questions. Follow the green path — if you keep answering “Yes,” you’ll end up where most enterprises belong.

Semantic Layer Decision Tree

✅ Stay BI-Tool-Centric When:

  • You’ve standardized on one primary BI tool
  • You have deep investment in existing semantic models
  • Business users own and validate metric definitions
  • Interactive query performance is critical
  • Your AI/agent strategy can sit on top of the BI model
  • You don’t have a genuine multi-tool consumption problem

⚠️ Consider Data-Platform-Centric When:

  • You genuinely operate 3+ BI tools with significant metric overlap
  • Your primary consumers are programmatic (APIs, agents, apps) rather than visual
  • You’re early in your analytics journey with no entrenched BI semantic model
  • You’re willing to accept the added complexity and governance overhead

Bottom Line

The data-platform semantic layer movement solves elegantly for a scenario that affects a minority of organizations, while introducing architectural complexity, governance fragmentation, and performance trade-offs for everyone else.

Before you rearchitect your semantic layer because you were told it’s “strategic,” ask the hard questions above. The answer for most enterprises today is: your BI tool’s semantic model is already the foundation of your semantic layer — and that’s not a weakness. It’s a huge strength.

For a deeper technical perspective on why stacking semantic models on top of each other doesn’t work, I highly recommend Chris Webb’s post Power BI And Support For Third Party Semantic Models. He explains the architectural reasons why — from SQL generation assumptions to metric aggregation conflicts — putting one semantic model on top of another creates fundamental problems that aren’t specific to any single tool.


Don’t let industry narratives make your architecture decisions. Let your users, your governance model, and your actual consumption patterns guide you.

PBI Fixer v2: From 11 Fixers to a Full Power BI Development Environment

PBI Fixer v2 Report Explorer with IBCS Variance

A few weeks ago, I published the first PBI Fixer article. It had 11 fixers — 5 for reports, 6 for semantic models — and a single promise: pbi_fixer(). One line of Python in a Fabric Notebook, and your reports get better.

That promise still holds. But the tool behind it has become something I did not anticipate. Built on top of Semantic Link Labs by Michael Kovalsky, which provides the core APIs that make all of this possible, the PBI Fixer has grown from a simple fixer into a full development environment.

45+ 12 19 0 1
Automated Fixers Interactive Tabs BPA Auto-Fix Rules License Cost Line of Python

What started as a fixer grew into a full Power BI development environment — a Semantic Model Explorer, a Report Explorer, a Memory Analyzer, a Perspective Editor, a Translations Editor, a Model Diagram generator, a Prototyping tool, BPA analyzers on both the model and report layer, a Delta Analyzer, and genuine IBCS implementation. All inside a Fabric Notebook. All driven by the same pbi_fixer() function call.

This article is the deep dive. I will walk you through every major capability, explain the reasoning behind the design decisions, and — because a picture is worth a thousand words — show you the screenshots.

🌐 Visit the PBI Fixer website for a quick overview: actionablereporting.com/pbi-fixer

1. The Evolution — How We Got Here

The PBI Fixer is the fifth generation of my attempt to codify Power BI best practices into reusable automation. Each generation taught me something that the next one built on:

2023 — Power BI Pimp Script (C# script): A single C# script for an external tool that automated calendar tables, measure tables, and calculation groups. The idea that best practices should be one-click actions, not documentation you read and then forget.

2024 — 80+ C# Macros: The Pimp Script expanded into a full library — 80+ macros organized by category, integrated with Stream Deck shortcuts. But still local, still C#, still semantic-model-only.

2025 — PBI Fixer PowerShell Edition (9 fixers): The first tool that touched both the report layer and the semantic model. PowerShell scripts that downloaded all reports from a workspace, replaced pie charts, applied themes, fixed page sizes, and re-uploaded them. Powerful, but fragile: Windows file dialogs, local execution, the report format was still opaque binary.

February 2026 — pbi_fixer() v1 (11 fixers): The move to the cloud. The introduction of the PBIR format and Fabric’s connect_report() API made it possible to read and modify report definitions programmatically — in a notebook, without downloading anything. 11 fixers, an ipywidgets UI, scan/fix/scan+fix modes. No local tools, no downloads, just pbi_fixer().

Now — PBI Fixer v2 (45+ fixers, 12 tabs): The current version. Not just fixers anymore — a complete development environment. The moment I realized that the same infrastructure (connect_report for PBIR, connect_semantic_model for TOM, ipywidgets for the UI) could power interactive exploration, editing, and analysis, the scope expanded naturally.

2. Getting Started — Still One Line

Before I dive into the tabs and features, let me make sure you can actually run this. It is still two cells in a Fabric Notebook:

# Cell 1: Install (run once per session, takes ~1 minute)
%pip install git+https://github.com/KornAlexander/semantic-link-labs.git@feature/pbi-fixer-ui

# Cell 2: Launch
from sempy_labs import pbi_fixer
pbi_fixer()

That is all. The interactive UI renders in the cell output. Type your workspace name, report name, and you are in.

You can also pre-fill the workspace and report:

# Load specific reports (comma-separated)
pbi_fixer(workspace="My Workspace", report="Sales Dashboard, Finance Report")

# Load ALL items in a workspace
pbi_fixer(workspace="My Workspace")

⚠️ Note: The PBI Fixer currently lives in a fork of Semantic Link Labs. It is not part of the official package yet — we’re evaluating if it makes sense. Once (if) it gets merged, the install becomes a true one-liner: just %pip install semantic-link-labs. Until then, use the fork URL above.

Prerequisites

  • A Microsoft Fabric capacity (F2 or higher, or a trial capacity)
  • A Fabric Notebook in your workspace
  • Reports in PBIR format for report fixers (the tool detects PBIRLegacy and can convert)
  • XMLA read/write enabled at the tenant level (required for semantic model operations)
  • Large semantic model storage format enabled in workspace settings (required for XMLA write)

3. Report Explorer — See Your Report, Fix Your Report

Report Explorer

The Report Explorer: tree view of pages and visuals with live embedded preview and fix actions

The Report Explorer is where most report-focused work happens. It loads your report’s PBIR definition and renders a navigable tree: Report → Pages → Visuals. Select any visual to see its properties — chart type, data fields, formatting. There is a live embedded preview panel so you can see what the visual looks like while editing its properties.

The key feature is the action dropdown at the top. This is where all 15 report fixers live:

# Fixer What It Does
1 Fix Pie Charts Replaces pie charts with clustered bar charts
2 Fix Bar Charts Removes axis clutter, adds data labels, removes gridlines
3 Fix Column Charts Same clean-up applied to column charts
4 Fix Line Charts Standardized line chart formatting
5 Fix All Charts Unified formatting pass across all chart types
6 Column → Line Converts column charts to line charts
7 Column → Bar (IBCS) Converts column charts to horizontal bar charts
8 Fix IBCS Variance End-to-end IBCS variance implementation
9 Fix Page Size Upgrades default pages to Full HD (1080×1920)
10 Hide Visual Filters Hides visual-level filters from consumers
11 Fix Visual Alignment Snaps misaligned visuals to consistent positions
12 Remove Unused Custom Visuals Cleans up unused custom visual registrations
13 Disable Show Items No Data Turns off “Show Items with No Data”
14 Migrate Report-Level Measures Moves report-level measures to the semantic model
15 Convert to PBIR Upgrades PBIRLegacy to PBIR format

Every fixer supports Scan mode — a read-only pass that tells you what would change without touching anything. This is essential for auditing: scan ten reports, get a findings list, then decide which ones to fix.

4. Semantic Model Explorer — Your Model Workbench

Model Explorer

The Model Explorer: object tree, DAX editing, data preview, with undo/redo tracking

The Model Explorer is a full workbench for your semantic model. It connects via XMLA and gives you a tree of everything: tables, columns, measures, hierarchies, relationships, calculation groups. Select any object to see its properties. Select a measure to edit its DAX expression and format string.

What sets it apart:

  • DAX editing — edit measure expressions directly, with a Save/Discard workflow
  • Create measures, calculated columns, and calculated tables — directly from the tree
  • Data preview — see Top 10 / Top 100 / All rows for any table with a single click
  • Auto-create measures from columns — the tool looks at SummarizeBy settings and generates explicit measures automatically
  • Add PY measures — select measures and get PY + ΔPY + ΔPY% generated for each
  • Undo / Redo — full change tracking, so you can roll back

This is not Power BI Desktop. It is not a replacement for external modeling tools. It is a focused tool that does the 80% of model editing work you actually need, running in the same notebook where you do your data engineering. No context switch. No download. No separate application.

5. IBCS — Where It Gets Interesting for Me

IBCS Variance Implementation

IBCS variance charts with proper green/red coloring, generated automatically from any existing bar or column chart

If you have read my other articles, you know that IBCS — the International Business Communication Standards — is a topic close to my heart. Clean, standardized business charts. No pie charts. No 3D effects. Horizontal bar charts for comparisons. Proper variance notation. Consistent scaling.

The PBI Fixer now has genuine IBCS implementation built in. And I don’t mean “it removes gridlines” — although it does that too. I mean end-to-end variance chart creation.

Here is what Fix IBCS Variance does, automatically, when you run it on a chart:

  1. Checks if a calendar table exists — if not, creates one (the same CalcCalendar from the semantic model fixers)
  2. Detects and builds the date relationship — finds the matching date column in your fact table and creates the relationship
  3. Generates PY measures — PY (prior year), ΔPY (absolute variance), ΔPY% (relative variance) for the measures used in the selected chart
  4. Adds error bar measures — creates the measures that drive the variance display
  5. Applies IBCS formatting — green/red coloring, proper axis configuration, data labels

A workflow that takes 30+ minutes by hand — done in seconds. The important thing: the resulting chart uses native Power BI visuals. No custom visuals, no marketplace dependencies.

The additional IBCS-aligned fixers complement this:

  • Column → Bar — flips vertical column charts to horizontal bar charts (IBCS prefers horizontal for categorical comparisons). Power BI Desktop does not have a “change orientation” button — the Fixer rewrites the visual JSON.
  • Fix All Charts — removes axis clutter, gridlines, adds data labels. This is what IBCS calls “decluttering” — making the data speak without chart junk.
  • Fix Pie Charts — replaces pie charts with bar charts. IBCS explicitly discourages pie charts.

These are not cosmetic tweaks. If you are serious about business reporting standards, this is the first time you can apply them programmatically across an entire report — not just read about what you should do, but actually do it with a function call.

6. Model BPA — 19 Rules, One-Click Auto-Fix

Model BPA

Model BPA: 19 best practice rules across 6 categories with Fix All / Fix Rule / Fix Row granularity

Best Practice Analyzers exist. External tools have them. Semantic Link Labs has run_model_bpa(). But here is the gap: most BPA tools tell you what is wrong. Some offer fix expressions for a handful of rules, but applying fixes at scale — across many rules, many objects, with granular control — is still cumbersome. And the majority of rules simply have no auto-fix at all.

The PBI Fixer’s Model BPA takes a different approach: every single one of its 19 rules comes with a one-click auto-fix button. And you can choose the granularity: fix everything with “Fix All”, fix a specific rule for all affected objects, or fix a single row. The underlying BPA engine in Semantic Link Labs was built by Michael Kovalsky — the PBI Fixer adds the interactive auto-fix layer on top.

Category Rules
🔢 Data Types Fix Floating Point Types (Double → Decimal)
📡 MDX Fix IsAvailableInMDX (False on non-attribute, True on key/hierarchy)
📝 Documentation Fix Measure Descriptions (auto-fill with DAX expression)
📊 Formatting Date format, Month format, Measure format, Percentage format, Whole number format, Flag column format, Sort Month Column, Data Category, Use DIVIDE(), Remove +0 prefix
🔗 Schema Hide Foreign Keys, Do Not Summarize, Mark Primary Keys
🏷 Naming Trim whitespace, Capitalize first letter

I cross-checked the PBI Fixer’s 19 auto-fixers against the community standard BPA rules: out of ~28 standard rules, only 7 have a FixExpression. Of those, just 4 overlap with the PBI Fixer’s rules (floating point types, IsAvailableInMDX, hide foreign keys, do not summarize). That means 15 of the PBI Fixer’s 19 auto-fixers — including all formatting rules, DIVIDE() correction, data categories, primary key detection, and naming fixes — have no equivalent auto-fix in the standard BPA ruleset.

7. Memory Analyzer — Vertipaq at Your Fingertips

Memory Analyzer

Column-level memory breakdown with color-coded percentage bars

The Memory Analyzer — built on top of SLL’s vertipaq_analyzer() by Michael Kovalsky — shows you where your model’s memory goes, broken down by tables, columns, partitions, relationships, and hierarchies. This is Vertipaq analysis built into the notebook, with color-coded size bars (red for columns taking >30% of total memory, orange for >10%, green for ≤10%).

For Direct Lake models, it also shows column residency and temperature — whether the data is hot (in memory), warm (cached), or cold (on disk). This is critical information for Direct Lake optimization that Desktop simply cannot show you.

8. Translations Editor — Multi-Language Metadata Management

Translations Editor

Grid view of all model objects × languages — add, edit, and manage translations in one place

If you work in a multi-language environment, this one is for you. The Translations Editor shows all model objects (tables, columns, measures) in a grid with a column for each language. You add a language from 20 built-in language codes, then edit translations directly in the grid — cell by cell. It is a manual process, but having all objects and all languages visible side by side makes it far more manageable than editing translations through the XMLA endpoint or third-party tools.

You can create new languages, delete existing ones, and see at a glance which objects are missing translations. No native Power BI tool gives you this kind of overview.

9. Perspective Editor — Visual CRUD for Perspectives

Perspective Editor

Expandable table tree with tri-state indicators: ● Full / ◐ Partial / ○ None

Perspectives are one of those features that everyone agrees are useful but nobody creates because the tooling is awkward. The Perspective Editor — inspired by the perspective management capabilities in SLL — makes it visual: an expandable tree with tri-state checkboxes. Select tables, columns, or measures for each perspective. Create new perspectives. Delete old ones.

If you are using Direct Lake and cache pre-warming (another fixer in the toolkit), perspectives are essential — the warm-up perspective defines exactly which columns to pre-load into memory.

10. More Tabs — Model Diagram, Prototyping, Delta Analyzer, Report BPA

Model Diagram

Model Diagram

Auto-generated SVG diagram with table boxes, columns, measures, and relationship lines

An auto-generated SVG diagram of your data model. Tables as boxes, columns and measures listed inside, relationships drawn as connecting lines with nearest-edge routing. This is not Power BI Desktop’s model view — it is exportable, embeddable, and generated in a notebook cell. Useful for documentation, architecture reviews, or simply getting a quick overview of an unfamiliar model.

Prototype & Excalidraw Export

Prototyping

Report page screenshots with auto-detected navigation and drillthrough paths

The Prototype tab captures every page of your report as a screenshot and detects page navigation links, buttons, and drillthrough connections between pages. You can export the whole thing as an Excalidraw file or SVG — giving you, for the first time, a bird’s eye view of your entire report on a single canvas.

I wrote a separate article about prototyping with AI where I used this feature to analyze a 35-page report and condense it to 18 pages in 10 minutes.

Delta Analyzer

Delta Analyzer

Parquet file and column chunk level inspection for Direct Lake optimization

If you are using Direct Lake, the Delta Analyzer is essential. Built on SLL’s delta table inspection capabilities by Michael Kovalsky, it inspects your lakehouse delta tables at the parquet file level — number of files, row groups, column chunks, column statistics, and cardinality. This is the data you need to decide whether your tables need V-Order optimization, repartitioning, or column type changes.

Report BPA

Report BPA

Report-level best practice analysis with auto-fix capabilities

While Model BPA focuses on the semantic model layer, Report BPA analyzes the report definition itself. It catches issues like unused custom visual registrations, “Show Items with No Data”, report-level measures (which should be in the model), and auto-converts PBIRLegacy before scanning.

11. Semantic Model Fixers — The Foundation

The 11 semantic model fixers use the XMLA endpoint and TOM (Tabular Object Model) to modify the data model directly. These are the original “one-click best practices” from the very first generation:

# Fixer What It Does
1 Discourage Implicit Measures Prevents implicit measure usage (required for calc groups)
2 Add Calendar Table 20-column calculated calendar with 3 hierarchies + auto-relationship
3 Add Measure Table Empty calculated table to centralize measures
4 Add Last Refresh Table M partition with refresh timestamp + measure
5 Add Units Calc Group Thousand & Million items with % skip logic
6 Add Time Intelligence Calc Group AC/Y-1/Y-2/Y-3/YTD + absolute/relative/achievement variances (21 items)
7 Measures from Columns Auto-create explicit measures from SummarizeBy settings
8 Add PY Measures PY + ΔPY + ΔPY% for selected measures
9 Setup Incremental Refresh Auto date column detection + policy configuration
10 Cache Pre-warm Direct Lake warm-up perspective + notebook generation
11 Prep for AI Descriptions, synonyms, linguistic metadata for Copilot readiness

Each of these runs through TOM and modifies the model’s metadata. Because XMLA write operations are irreversible (you cannot download the model as .pbix afterward), the UI requires an explicit confirmation before running semantic model fixers. Scan mode is always safe.

Cache Pre-Warm — Why This Matters for Direct Lake

Direct Lake is powerful because it reads directly from delta tables without importing data. But there is a cold-start problem: the first query against a model after a refresh (or after columns get evicted from memory) can be significantly slower because the data needs to be loaded from OneLake into the Vertipaq engine on demand. This is especially noticeable on models with many columns or wide fact tables.

The Cache Pre-warm fixer solves this by generating two things: a perspective containing only the columns that are actually referenced by DAX measures (so you are not warming up unused columns), and a notebook cell that runs a simple DAX query against that perspective — forcing the engine to pre-load those columns into memory. You can schedule this notebook to run right after your data refresh pipeline, so by the time users open the report, the data is already hot. Zero-effort, production-grade cache management.

Setup Incremental Refresh — Auto-Detection Done Right

Configuring incremental refresh manually is tedious: you need to identify the right date column, create RangeStart and RangeEnd parameters, modify the partition expression, and set the refresh window. The Setup Incremental Refresh fixer automates this entire process. It scans your model for date columns, lets you pick the right one, and configures the incremental refresh policy — including the historical window and the rolling refresh window. What used to be a multi-step, error-prone manual process becomes a single click with sensible defaults.

Prep for AI — Getting Copilot-Ready

Microsoft Copilot in Power BI works better when your semantic model has rich metadata: measure descriptions, column descriptions, synonyms, and linguistic schema info. But most models have none of this — descriptions are empty, synonyms are missing, and the model is essentially a black box to the AI.

The Prep for AI fixer generates all of this metadata automatically. It creates descriptions for measures (based on their DAX expressions), adds synonyms for columns and tables (common abbreviations and alternative names), and populates the linguistic schema that Copilot uses for natural language understanding. This is not just cosmetic — it directly improves the quality of Copilot-generated DAX queries and natural language answers. Going from zero to Copilot-ready in one click. Credit for the original idea and implementation goes to Lukasz Obst.

12. The Standalone API — Every Fixer is a Function

Everything you see in the UI is a Python function underneath. You do not need the interactive widget to use the fixers — you can call them directly in your own notebooks, scripts, or pipelines:

# Replace all pie charts in a report
from sempy_labs.report._Fix_PieChart import fix_piecharts
fix_piecharts("Sales Report", workspace="Production")

# Scan first (read-only)
fix_piecharts("Sales Report", workspace="Production", scan_only=True)

# IBCS variance — end to end
from sempy_labs.report._Fix_IBCSVariance import fix_ibcs_variance
fix_ibcs_variance("Sales Report", workspace="Production")

# Batch process multiple reports
reports = ["Sales", "Finance", "HR"]
for report in reports:
    fix_piecharts(report, workspace="Production")
    fix_page_size(report, workspace="Production")

This is where the AI and CI/CD story comes in. Because every fixer is a stateless function with a predictable signature (report, workspace, scan_only), an AI agent could generate the right call from a natural language prompt. A deployment pipeline could run scan-only passes as quality gates. A Fabric scheduled notebook could batch-fix formatting across all reports in a workspace overnight.

The functions are the same. The caller changes.

13. Built on Semantic Link Labs

I want to be clear about the foundation: the PBI Fixer is built on top of Semantic Link Labs, created by Michael Kovalsky. SLL provides the core abstractions — connect_report() for PBIR access, connect_semantic_model() for TOM/XMLA, and the notebook infrastructure that makes all of this possible in a Fabric environment.

Without SLL, the PBI Fixer would not exist. The fixer adds the interactive UI layer, the specific fix logic, the BPA rules, and the explorer/analyzer capabilities — but the engine underneath is Michael’s work. Credit where it is due.

The PBI Fixer is currently in a fork of SLL. We are evaluating whether it makes sense to merge it into the main repository. If that happens, %pip install semantic-link-labs would be all you need — the fork URL goes away, and it truly becomes one line.

14. What This Is Not

Let me be honest about the limitations:

  • Not a Desktop replacement — the PBI Fixer does not have a drag-and-drop report canvas. You cannot create new visuals from scratch (yet). It extends Desktop; it does not replace it.
  • Still beta — this is an early-stage tool with rough edges. Test on a non-production workspace first.
  • Requires Fabric — this runs in Fabric Notebooks. If you are on Power BI Pro without Fabric, this tool is not available to you.
  • XMLA write is irreversible — once you modify a model via the XMLA endpoint, you can no longer download it as a .pbix with embedded data. This is a platform limitation, not a PBI Fixer limitation.

That said — if you have Fabric, and you care about report quality, standardized formatting, and Best Practice adherence, I believe this is the most comprehensive tool available right now. And it is free.

15. “But Can’t AI Just Do This?”

Fair question. With Copilot in Fabric, ChatGPT writing Python, and agentic AI on the horizon — why build a UI tool at all?

Because AI is great at generating code when you know what to ask for. But most Power BI developers do not know that their model has floating-point columns, that 12 columns have SummarizeBy set to Sum, or that their report still uses pie charts. Discovery comes first — and that requires a structured UI that surfaces issues you did not know existed.

The PBI Fixer handles both sides. The interactive UI is for discovery: browsing a model tree, scanning for BPA violations, spotting memory hogs in the Vertipaq analysis, seeing which columns lack translations. You do not need to know what to look for — the tool shows you.

And once you know what to fix, the standalone API is where AI shines. Every fixer is a stateless Python function with a predictable signature. An AI agent can call fix_piecharts("Sales Report") just as easily as a human can click “Fix All”. A deployment pipeline can run scan_only=True passes as quality gates. A scheduled notebook can batch-fix formatting across an entire workspace overnight.

Today, you use the UI. Tomorrow, an AI agent runs the same functions. The PBI Fixer is not competing with AI — it is the toolkit AI will use.

16. What is Next?

The framework is open. Every fixer is an independent Python function with a consistent interface. Adding a new fixer means writing one function and wiring it into the UI. If you have an idea for a fixer — or you want to contribute — the GitHub repository is open.

This is mostly a free-time project — I work on it when I can, driven by curiosity and the feedback I get. If time allows, there is plenty more to build: more IBCS implementation, more BPA rules, video walkthroughs, and the ongoing evaluation of whether merging into Semantic Link Labs makes sense.

The first PBI Fixer article ended with:

The PBI Fixer is not just a tool — it is a shift in how we think about report quality. Instead of manually checking and fixing every visual property, every model setting, every best practice after the fact, we can now codify our standards and apply them at scale.

A few weeks later, that statement holds — but the scale has changed. From 11 fixers checking a few properties to a full development environment covering reports, models, BPA, memory, translations, diagrams, and prototyping. Still one line.

pbi_fixer()

If you try it, let me know on LinkedIn. Feedback — positive and negative — is what drives this project forward.

Automated Semantic Model Health Checks: BPA + Memory Analyzer + Lakehouse in One Fabric Notebook

Automated Semantic Model Health Checks with Fabric Notebook

Every Power BI semantic model starts clean. But over time — as measures pile up, relationships multiply, and formatting inconsistencies creep in — models silently degrade. By the time someone notices a slow report or a questionable DAX pattern, the damage is already done.

What if your models could check themselves — every week, automatically — and write the results to a place where you can query, compare, and trend them over time?

That’s exactly what this Fabric Notebook does. It combines three analyses into one script, saves everything to a Lakehouse, and runs on a schedule. No manual effort after the initial setup.

Why Run BPA and Memory Analyzer Together?

Microsoft Fabric comes with two built-in analysis notebooks: the Best Practice Analyzer (BPA) and the Memory Analyzer (also known as Vertipaq Analyzer). Most people run them separately, if they run them at all. But running them together gives you the full picture of your model’s health:

  • BPA catches structural problems — missing format strings, duplicate measures, hidden foreign keys not set, unused objects, DAX anti-patterns. It tells you what’s wrong with how the model is built.
  • Memory Analyzer shows you where the weight is — which tables, columns, and segments consume the most memory. A model can pass every BPA rule and still be bloated because of high-cardinality columns or unnecessary detail data.

Together, they answer both questions: “Is this model built correctly?” and “Is this model built efficiently?”

On top of that, the script also captures seven INFO.* DAX system views — tables, columns, measures, relationships, storage tables, storage columns, and storage segments. These are the raw metadata that power tools like DAX Studio’s Vertipaq Analyzer, but now they live in your Lakehouse as queryable Delta tables.

Direct Lake Models: What You Need to Know

This notebook works with both Import and Direct Lake semantic models — but the Memory Analyzer results mean different things depending on which mode you’re using.

Import Models: Stable, Predictable Memory

For Import models, memory analysis is straightforward. The entire dataset is loaded into memory at refresh time and stays there. When you run Memory Analyzer, you see the full footprint — every table, every column, every segment. The results are stable and reproducible.

Direct Lake Models: Dynamic, Query-Driven Memory

Direct Lake models load data on demand — only the columns and rows that are actually queried get paged into memory. This means the Memory Analyzer shows you a snapshot of what’s currently cached, not the theoretical maximum size of the model.

If you run the analysis right after opening the semantic model, you’ll see almost nothing in memory — the cache is cold. To get realistic memory metrics for a Direct Lake model:

  1. Warm up the cache first. Run your most common reports or execute representative DAX queries to load the frequently accessed columns.
  2. Run the notebook after typical usage. Schedule it for a time when the model has been actively queried during business hours — not at 3 AM when the cache is empty.
  3. Interpret the results as “working set” size. The memory footprint you see reflects what’s needed to serve recent queries, not the full lakehouse table size. This is actually more useful for capacity planning — it tells you the realistic memory demand.

BPA rules, on the other hand, work identically for both Import and Direct Lake — they analyze the model’s structure (measures, relationships, formatting), not the data in memory.

The Three-Step Pipeline

The notebook runs sequentially through three steps:

Step 1: Best Practice Analyzer

Uses fabric.run_model_bpa() to evaluate the semantic model against the standard rule set. Results include the rule category (Formatting, Maintenance, Performance, DAX Expressions), severity, affected object, and a description of the issue.

BPA Results showing severity breakdown by category
BPA output — 116 rules checked across DAX Expressions (12), Formatting (27), Maintenance (45), and Performance (15) categories with severity breakdown

Step 2: Memory Analyzer

Uses fabric.model_memory_analyzer() to produce a tabbed summary: Model Summary, Tables, Partitions, Columns (Total Size), Columns (Temperature), Relationships, and Hierarchies. This is the same analysis DAX Studio provides — but running directly inside Fabric.

Memory Analyzer output with Model Summary
Memory Analyzer — Model Summary showing dataset size (381.91 KB), 7 tables, 27 columns, and INFO table results

Step 3: DAX INFO Views

Seven EVALUATE INFO.*() queries capture the full internal metadata of the model and save each as a Delta table:

  • INFO.TABLES() → table-level metadata (row counts, modified times, storage IDs)
  • INFO.COLUMNS() → column-level details (data types, cardinality hints)
  • INFO.MEASURES() → all measures with their DAX expressions
  • INFO.RELATIONSHIPS() → relationship definitions and cardinality
  • INFO.STORAGETABLES() → physical storage table metadata
  • INFO.STORAGETABLECOLUMNS() → storage-level column statistics
  • INFO.STORAGETABLECOLUMNSEGMENTS() → segment-level compression details

Each result set gets an analysis_timestamp, model_name, and workspace_name column appended before saving — so you can track multiple models over time in the same Lakehouse.

Why Save to a Lakehouse?

Running an analysis notebook and looking at the output once is useful. But the real value comes from persistence:

  • Trend analysis. When BPA results are appended weekly, you can track whether your model is getting better or worse. Did someone introduce 12 new unformatted measures last sprint? You’ll see it.
  • Cross-model comparison. Run the same notebook against multiple semantic models (just change the configuration variables). Query all results in one place to find which models need the most attention.
  • SQL Analytics Endpoint. Every Delta table in a Fabric Lakehouse is automatically exposed via a SQL endpoint. That means you can query BPA results from SSMS, Azure Data Studio, or even build a Power BI report on top of your model health data.
  • Audit trail. When stakeholders ask “how has model quality improved this quarter?”, you have the data to answer.
SQL query results from bpa_analysis_results table
Querying bpa_analysis_results via SQL in SSMS — DemoLakehouse with all 8 Delta tables, showing Category, Rule_Name, Severity, and other columns

Why Schedule It?

A one-time health check is a snapshot. A scheduled health check is a monitoring system.

Fabric Notebooks support native scheduling — no Data Factory pipeline needed. From the top ribbon, click Schedule, set it to Weekly, pick a day and time, and save. The notebook runs unattended, appends results to your Lakehouse, and you’re done.

Fabric Notebook schedule configuration
Schedule setup — Weekly on Monday at 12:00 PM, Brussels timezone, running from April 2026 to April 2027

This means:

  • No manual intervention. Models are checked every week without anyone clicking “Run”.
  • Early warning system. New BPA violations show up within a week of being introduced.
  • Memory regression detection. If a model’s memory footprint suddenly doubles after a data source change, the INFO tables will show it in the next scheduled run.
  • Compliance documentation. Some organizations require periodic checks on data model quality. A scheduled Lakehouse write creates an immutable record.

Setup: Five Minutes to Automated Model Monitoring

Here’s the complete setup process:

  1. Create a Lakehouse in your Fabric workspace (or use an existing one).
  2. Create a new Notebook and attach the Lakehouse as the default (left panel → Lakehouses → Add).
  3. Paste the script from GitHub or the Fabric Community Notebook Gallery.
  4. Set the three config variables:
dataset = "Your Semantic Model Name"
workspace = "Your Workspace Name"
lakehouse = "Your_Lakehouse_Name"
  1. Run once to verify everything works.
  2. Schedule via Run → Schedule → Weekly.

The full notebook code is available on GitHub.

What You Get: 8 Delta Tables

After a successful run, your Lakehouse contains:

Table Source Content
bpa_analysis_results BPA All rule violations with severity, category, object info
info_tables INFO.TABLES() Table-level metadata (row counts, storage IDs, modified times)
info_columns INFO.COLUMNS() Column definitions, data types, cardinality
info_measures INFO.MEASURES() Measure names and DAX expressions
info_relationships INFO.RELATIONSHIPS() Relationship definitions and cardinality types
info_storage_tables INFO.STORAGETABLES() Physical storage table metadata
info_storage_columns INFO.STORAGETABLECOLUMNS() Storage-level column statistics
info_storage_segments INFO.STORAGETABLECOLUMNSEGMENTS() Segment-level compression and encoding details

Each table includes analysis_timestamp, model_name, and workspace_name columns — so you can filter, compare, and trend across models and time periods.

Credits

The DAX INFO.* views approach was inspired by Hariharan’s Vertipaq Analyzer queries. SQL view equivalents are available in my PBI-Tools repository. Thanks to Bernat Agulló for pointing me in the right direction.

Resources

Power BI to Fabric: Seven Reasons to Upgrade — Told as an Aviation Story

Why moving to Microsoft Fabric is like upgrading from a small airstrip to a world-class airport

Preamble: This article is not a comprehensive migration guide. It is a narrative — a story told through an aviation metaphor — that illustrates why Microsoft Fabric transforms Power BI from a capable tool into a professional, future-proof analytics platform. The views expressed here are my own and do not represent official Microsoft guidance.

If you want a deep technical walkthrough of specific Fabric capabilities for Power BI, check out my earlier article: Power BI Without Fabric Is Like Owning a Sports Car but Leaving It in Eco Mode.


Overview — Five pillars of Fabric for Power BI, depicted as an airport with different zones

The full picture: Seven areas where Fabric elevates your Power BI environment

Imagine your Power BI environment is an airline. Reports are your fleet, data models are your engines, and your team — the analysts, developers, admins — are the pilots and ground crew keeping everything in the air.

For years, the operation has worked. Flights depart, passengers arrive, the business gets its data. But behind the scenes, things are starting to strain. Deployments feel like taxiing without instruments. Troubleshooting means searching through scattered flight logs. And when someone asks “can we do AI?”, you realize your runway is too short for the next generation of aircraft.

Microsoft Fabric is the airport upgrade. Not a different airline — the same planes, the same crew — but with a modern terminal, a control tower, proper hangars, and a runway long enough for whatever comes next.

Let me walk you through seven areas where this upgrade changes everything.


1. Training in the Flight Simulator — A Professional BI Environment

Flight simulator training — symbolizing professional development lifecycle with DEV, QA, and PRD stages

Before your reports go live, they train in the simulator

No airline would let a pilot fly passengers after just one practice run. Yet in many Power BI environments, that’s exactly what happens: changes go straight to production. A modified DAX measure, an updated data model, a new page — deployed directly to the live report that hundreds of users rely on.

Fabric introduces the flight simulator — a proper DEV → QA → PRD lifecycle.

  • Development (DEV): Build and experiment freely in an isolated workspace. Break things. Try new approaches. No one sees it until you’re ready.
  • Quality Assurance (QA): Test with real data, validate with stakeholders, catch issues before they matter.
  • Production (PRD): Deploy with confidence through Deployment Pipelines — automated, controlled, reversible.

On top of this, Fabric enables full Git integration. Every change to a report, semantic model, or notebook is version-controlled. You can see who changed what, when, and why. And if something goes wrong? Rollback in seconds — not hours of panicked troubleshooting.

Add CI/CD pipelines with code review, and you have a deployment process that matches what software engineering teams have enjoyed for decades. No more manual risk. No more “who published that change?”

In aviation terms: Your pilots now train in a simulator before every flight, and every procedure is documented in a flight manual that the entire crew can reference.


2. Upgrades in the Hangar — Better Power BI Reports

Hangar with mechanics upgrading aircraft — symbolizing report optimization with Vertipaq, BPA and templates

The hangar is where your reports get tuned for peak performance

Every aircraft needs regular maintenance. Engines are inspected, instruments recalibrated, performance data reviewed. Without a proper hangar? Maintenance happens on the tarmac, in the rain, with improvised tools.

That’s been the reality for many Power BI environments. Analyzing model performance meant installing third-party tools like Tabular Editor and DAX Studio, configuring XMLA endpoints, and relying on experts who knew the process by heart. Powerful — but fragile and inaccessible to most team members.

Fabric changes this by integrating Vertipaq Analyzer and Best Practice Analyzer (BPA) natively into the platform. What used to require multiple tools and expert configuration now takes just a few clicks:

  1. Open your semantic model in Fabric
  2. Click on BPA or Memory Analyzer
  3. Run the notebook

That’s it. The same deep analysis — column-level memory consumption, rule violations, data type issues — now accessible to the entire team.

But the hangar offers more than diagnostics. With Fabric Notebooks, you can automate report optimization — fix formatting issues, apply best practices, even replace poor visualization choices programmatically. And with pre-built templates, new reports start from a solid foundation instead of a blank canvas.

In aviation terms: Your hangar now has state-of-the-art diagnostic equipment, automated maintenance routines, and a parts catalog that ensures every new aircraft starts with the right configuration.

Deep Dive: For a detailed walkthrough of BPA and Vertipaq Analyzer in Fabric, see my earlier article: Power BI Without Fabric Is Like Owning a Sports Car but Leaving It in Eco Mode — Section 1: Model Diagnostics.

3. Cargo Jet Capacity — OneLake for Everyone

Cargo jets being loaded at the airport — symbolizing OneLake, Direct Lake, and data access at scale

OneLake: One airfield, all cargo — no matter where it originates

Ever been at the airport with a suitcase that’s 24 kg? Sorry, 23 kg max — you’re not getting on that plane. Repack, leave something behind, or pay extra.

That’s Power BI Pro with its 1 GB model limit. Your data doesn’t fit? Too bad — repack, split, or leave data behind. And on top of that: max 8 refreshes per day, data duplicated across models.

OneLake — Fabric’s unified data layer — changes this fundamentally:

  • Direct Lake instead of Import: Reports can query data directly from the lakehouse without importing it. Real-time freshness, no duplication, no artificial model size limits.
  • Shortcuts & Mirroring: Connect external data sources — Snowflake, SQL Server, Azure SQL — without copying data. The data stays where it lives; Fabric provides a unified access layer.
  • No more 8-refreshes-per-day limit: With Direct Lake, your reports always reflect the latest data. No scheduling gymnastics, no “the data is from this morning” disclaimers.
  • Break the 1 GB barrier: Large datasets that previously required Premium capacity or complex workarounds now have room to breathe.
  • Cross-team collaboration: Different departments share data through Lakehouses and Shortcuts instead of building isolated silos. Finance can access the same data foundation as Operations — governed, consistent, up-to-date.

In aviation terms: Instead of every plane carrying its own cargo hold, you now have a central logistics hub. Every flight can access what it needs, when it needs it — whether the cargo originated locally or was shipped in from an external partner.


4. The Control Tower — Monitoring & Governance

Airport control tower overseeing all operations — symbolizing monitoring, lineage, and governance

The control tower sees everything: performance, lineage, impact, security

A small regional airport has one person with binoculars, a radio, and a weather app. It works — until traffic grows. Then you need a proper control tower with radar, flight tracking, and a full operations team.

That’s the difference between Power BI without Fabric and with it.

Without Fabric, a user reports: “My report shows an error.” Your options? Basically none. No logs, no query history, no details. You can’t reproduce it, you can’t trace it, you can’t help — unless the error happens to occur again right in front of you.

With Fabric, you get the full control tower:

  • Workspace Monitoring: Every query, every error, every performance spike — logged and traceable. A user reports a problem from yesterday? You look it up, see the exact DAX query, the filter context, and what went wrong. Retrospective analysis becomes possible.
  • Automated quality checks: Scan all reports and semantic models for rule violations, performance issues, and configuration problems — continuously, not just when someone remembers to check.
  • Lineage & Impact Analysis: Before changing a data source, a column, or a measure — see exactly which reports and dashboards are affected. No more “we didn’t know that report depended on this table.”
  • OneLake Security: One unified security model across all data. Define access rules once; they apply everywhere — across Lakehouses, Warehouses, and Semantic Models.

In aviation terms: Your airport now has radar, flight tracking, runway management, and air traffic control — all in one tower. No more flying blind.

Deep Dive: For a detailed walkthrough of Workspace Monitoring, FUAM, and Operation ID tracing, see: Power BI Without Fabric Is Like Owning a Sports Car but Leaving It in Eco Mode — Section 2: Monitoring and Troubleshooting.

5. AI-Ready — The Data Foundation

Data flowing into AI models — symbolizing OneLake as the foundation for ML and AI workloads

AI is no longer a future project — but without the right data foundation, every initiative remains patchwork

What does it mean when an airplane is “ready”?

It depends on who you ask:

  • The pilot says: preflight checks are done
  • Maintenance says: the plane is repaired and certified
  • Operations says: the plane is assigned to a flight
  • The CEO says: the plane is in our fleet

Same plane — four different meanings of “ready.” Without a shared understanding, communication breaks down.

That’s exactly the challenge with data. What does “revenue” mean? Ask Sales, Finance, and Operations — you’ll get three different answers.

This is where Ontology comes in: a shared understanding of your business — for humans AND for AI. Your semantic models become that ontology. Everyone speaks the same data language.

Fabric makes your data AI-ready from day one:

  • Central Data Lake for ML/AI: All data in OneLake, immediately available for Machine Learning and AI models. No complex data preparation, no separate pipelines. Data that powers your reports today can train models tomorrow.
  • Semantic Layer as Ontology: Your semantic models define what business terms mean. That shared understanding is what makes AI responses accurate and trustworthy.
  • Open Standards: Data in OneLake is stored as Delta-Parquet. Any ML framework — Python, Spark, R — can access it directly. No vendor lock-in, no format conversions.

This is perhaps the most future-critical argument: organizations that don’t centralize their data now will struggle to leverage AI later. Fabric doesn’t force you into AI workloads — but it ensures the foundation is there when you’re ready.

In aviation terms: You’re not just building runways for today’s aircraft. You’re laying the foundation — fuel systems, navigation infrastructure, clearance protocols — for the next generation of autonomous, intelligent flight.


6. AI-Ready — Copilot & Data Agents

Copilot in the cockpit — symbolizing AI-assisted analytics with Copilot and Data Agents

The co-pilot is ready: AI-assisted analytics for everyone on board

Every pilot has a co-pilot. So should you.

The data foundation is in place. Now comes the part your business teams will love.

  • Copilot Built Right In: In Power BI and Fabric Notebooks, analyze data and create reports using natural language. No DAX or SQL required. Available starting from F2 capacity.
  • Data Agents: One agent that speaks all the languages: DAX, SQL, GraphQL — you don’t need to know which one. Ask a question, the agent figures out the right query language behind the scenes.
  • Reuse Everywhere: Build a Data Agent once, reuse it across Fabric, Foundry IQ, Microsoft 365 Copilot, and any MCP-compatible tool. An MCP endpoint is created automatically with each agent. Zero extra setup.
  • Custom Instructions at Every Level: Want the agent to respond in Chinese? Set it in the agent instructions. But it gets even better: each connected data source can have its own instructions — business context, naming conventions, query hints. Global behavior + source-level precision.
  • From Question to Insight in Seconds: Instead of waiting weeks for a new report, users interact with data directly. The semantic model becomes the central knowledge layer — not just for reports, but for AI-powered analytics.

In aviation terms: Your airport now has a co-pilot in every cockpit. Not replacing the pilot — but augmenting their capabilities, handling routine tasks, and making the entire operation smarter.


7. Fabric Grows with You

Airport growing from small airstrip to major hub — symbolizing Fabric's scalability

Start small, scale intentionally — Fabric grows with your ambitions

The last point might be the most important one: Fabric doesn’t ask you to go big on day one.

Here’s what surprises many people: even the smallest Fabric capacity — an F2 — already unlocks the full feature set. Deployment Pipelines, Git Integration, Workspace Monitoring, Direct Lake, Copilot, Data Agents — all available. No Premium Per User licenses needed. No feature gating. Everything from the previous six sections works on the smallest SKU.

That changes the adoption conversation entirely. You don’t need executive sponsorship for a large capacity commitment. Start with an F2 or F4. Pick one workspace, one high-value use case. Let your team experience the difference. Then, as value becomes visible, scale intentionally:

  • Add more workspaces to the capacity
  • Enable Direct Lake for high-traffic reports
  • Introduce Notebooks for automation
  • Onboard Data Engineers, Data Scientists, Real-Time Engineers onto the same platform
  • Cover all use cases — ETL, Notebooks, Planning, Warehouses, Lakehouses — and reduce shadow IT

In many cases, a small F-series capacity is even cheaper per user than adding PPU licenses once you have more than a small group of users. Fabric is not a niche solution — it’s a single platform for all data consumers. All use cases covered — ETL pipelines, Notebooks, Warehouses, Lakehouses, Eventstreams, Planning — all in one ecosystem. That means less shadow IT, more control, and a future-proof architecture that evolves with every Fabric update.

The recommendation is simple:
Equip all your workspaces with Fabric capacity.

The sooner you switch, the sooner you benefit — from better performance, less operational overhead, and a platform that’s ready for whatever comes next.


This article complements my earlier deep-dive: Power BI Without Fabric Is Like Owning a Sports Car but Leaving It in Eco Mode. That article goes into technical detail on BPA, Vertipaq Analyzer, Workspace Monitoring, and Premium feature access. This article tells the story from 10,000 feet — the altitude where the bigger picture becomes clear.

Microsoft Fabric — Why You Should Enable Capacity Overage And How to Combine it with Surge Protection

If you manage a Microsoft Fabric capacity, there’s a good chance your users have already experienced throttling — 20-second delays, rejected queries, or broken dashboards — without you even knowing. The root cause is usually a temporary CU spike that pushes the capacity past its limits. The fix? Capacity Overage — a zero-cost insurance policy that automatically absorbs those spikes before they impact users.

In this article, I’ll explain what happens without overage, how to enable it, and how to combine it with Surge Protection for comprehensive capacity resilience.

The Problem: What Happens Without Overage

Fabric uses a smoothing mechanism to spread compute (CU) consumption over time. When usage exceeds what the capacity can handle, throttling kicks in — and it gets progressively worse:

  • 20-second delays on every interactive request. When smoothed usage exceeds 10 minutes of future capacity, Fabric adds a 20-second wait to every dashboard query, report load, and ad-hoc analysis. Users notice immediately.
  • Full rejection of interactive workloads. Once the 60-minute threshold is breached, all interactive requests — report views, DAX queries, dataflow previews — are rejected outright. Users see error messages instead of data.
  • Background jobs blocked for up to 36+ hours. At the 24-hour threshold, even scheduled refreshes, pipelines, and Spark jobs are rejected. Recovery can take 1.5× the overload window — meaning a capacity at 250% background rejection needs ~36 hours to recover, during which nothing runs.
  • Cascading user frustration & lost trust. End users don’t see “CU exhaustion” — they see broken dashboards, failed refreshes, and unresponsive reports. Repeated incidents erode confidence in the platform and drive shadow IT.

The Solution: How Capacity Overage Helps

  • Automatic throttling prevention. Overage kicks in the moment the interactive delay threshold (>100%) is crossed. It pays off excess CU debt so delays and rejections never reach the user.
  • Zero standing cost. There is no charge for having overage enabled. You only pay when it actually activates — and only for the CU-hours that would have caused throttling.
  • Admin-controlled spending limit. Set a rolling 24-hour CU-hour cap (in multiples of 48 CU). Once the limit is reached, standard throttling resumes — so costs are always predictable and bounded.
  • Instant activation, no restart required. Overage becomes active within 5 minutes of enabling. No capacity pause/resume, no workspace migration. Toggle it on and it works.
  • Works alongside Surge Protection. Overage handles interactive spikes; Surge Protection limits background job accumulation. Together they provide comprehensive capacity resilience.

Cost Perspective: Overage vs. Scaling Up

Overage charges at 3× the pay-as-you-go rate, but only for excess CU-hours actually consumed. For occasional spikes this is significantly cheaper than permanently over-provisioning by upgrading to the next SKU.

Rule of thumb: Keep the overage limit at ⅓ of your daily CU-hours — that is the break-even point with scaling up to the next SKU size.

ScenarioWithout OverageWith Overage
Occasional spike (1–2× /week)Users hit 20s delays or rejection; admins firefight manuallyAuto-resolved; users unaffected; small pay-per-use charge
Major spike (e.g., month-end)Hours of rejection; 36h+ recovery; escalations to ITOverage absorbs burst up to limit; workloads continue; clear cost trail
Sustained overloadThrottled until optimized or scaled upBuys time to react; doesn’t replace proper sizing — scale up if recurring

When to Use — and When Not

  • Ideal for: Rare unexpected spikes, small routine overloads, business-critical dashboards that cannot tolerate delays, capacities shared across multiple teams.
  • Not a substitute for: Persistent under-sizing (scale up instead), workload optimization, or dedicated capacity isolation for mission-critical workloads.

Surge Protection: Prevent Runaway Background Jobs

Surge Protection is a complementary capacity setting with two controls:

  • Workspace Consumption (interactive + background): Set a per-workspace CU limit as a percentage of total capacity over a rolling 24-hour window. When a workspace exceeds the threshold, it is blocked and all operations are rejected. Each workspace can be tagged as:
    • Available (default): follows surge protection rules
    • Mission Critical: exempt from workspace-level blocking, keeps running during spikes
    • Blocked: all operations rejected (manual or automatic)
  • Background Operations (background only): Set a background rejection threshold (e.g., 60%) so background jobs are rejected before the capacity enters deep throttling (normally at 100%). This frees up CU for interactive use and prevents long recovery times. Does not directly protect interactive requests.

Combined Strategy: Surge Protection + Overage

You can combine both features on a single capacity to protect specific workspaces from each other — for example, a development workspace from a production one, both on the same capacity:

  1. Enable workspace-level surge protection with a CU % threshold per workspace.
  2. Mark your critical workspaces as Mission Critical — they are exempt from workspace-level blocking.
  3. Leave other workspaces as Available — they get auto-blocked before causing capacity-wide throttling.
  4. Enable Capacity Overage — catches remaining interactive spikes for your mission-critical workspaces.

Result: Non-critical workspaces get blocked by surge protection before they can cause overage billing. Overage effectively only fires for mission-critical workspaces. Surge protection reduces overage activation frequency, keeping costs low.

Note: Capacity-level throttling still applies to all workspaces once CU limits are fully exhausted. Mission Critical status only overrides workspace-level surge protection, not capacity-level limits. Overage handles that gap.

Recommendation & How to Enable

Enable Capacity Overage on every production F16+ capacity with a conservative spending limit (⅓ of daily CU-hours). This provides a zero-cost insurance policy when idle and an automatic safety net when spikes occur — protecting user experience without requiring admin intervention.

  1. Admin Portal → Capacity Settings → select your capacity
  2. Expand “Capacity Overage” → toggle On
  3. Set a 24h CU-hour spending limit (start conservative, e.g. 48–96 CU-hours)
  4. Click Apply — active within 5 minutes
  5. Monitor via Capacity Metrics app + Azure Cost Management
Fabric Admin Portal — Capacity Overage settings showing the toggle, spending limit, and Apply button
Fabric Admin Portal — Capacity Overage settings

Jump & Run — Fabric for Kids

My kids and I vibe coded a jump & run game over the weekend. What started as a Python script turned into a full Fabric analytics pipeline — with a playable game running inside a notebook, stats saved to a Delta table, and a Power BI report on top.

Here is the game. Click the canvas below and start playing.

🎮 Play the Game

Controls: Arrow Keys / WASD to move · Space / W to jump · F to attack · Stomp enemies from above

If the embed does not load, open the game directly: https://kornalexander.github.io/jump-and-run/jump_and_run_v2.html


How It Started

My three boys wanted to build a game. We started with Python and Pygame — a classic jump & run with platforms, enemies, coins, water hazards, and a dragon boss at the end. The character collects power-up items (fox, egg, bat) that change its form and unlock different attacks: a lightning strike or a sonic wave.

It worked, but Pygame means installing Python locally. I had a different idea: what if we could play it inside a Microsoft Fabric notebook?

One Line to Play

I rewrote the entire game in HTML5 Canvas and JavaScript. The game logic — physics, collision detection, level generation, enemy AI, particle effects, audio — is a single self-contained HTML file. No external dependencies, no CDN links, no server.

Embedding it in a Fabric notebook is one line:

displayHTML(open("./builtin/jump_and_run.html").read())

Upload the HTML file to your Lakehouse Files, run the cell, and the game renders right inside the notebook output. My boys could not believe it.

The V2: Professional Polish

After the first version worked, I could not stop. The V2 adds:

  • Web Audio API — retro sound effects for jumps, coins, stomps, attacks, and the victory fanfare
  • Particle system — coin sparkles, death bursts, attack trails, ambient fireflies
  • Smooth camera — lerp-based follow cam that keeps the action centered
  • Screen shake — on hits and attacks for extra game feel
  • Parallax background — layered mountains and stars that move at different speeds
  • Gradient rendering — sky gradients, glowing water, soft lighting on platforms

All of it in ~1000 lines of vanilla JavaScript, no frameworks, no build step.

The Stats Pipeline

A game without stats is just — playing. But we are in Fabric. So I added a stats tracker.

After every game-over (win or lose), the game generates a JSON blob with 17 metrics:

{
  "duration_seconds": 142.5,
  "score": 3850,
  "won": true,
  "lives_left": 2,
  "deaths_total": 1,
  "deaths_enemy": 0,
  "deaths_water": 1,
  "deaths_fall": 0,
  "coins_collected": 24,
  "enemies_stomped": 5,
  "enemies_zapped": 3,
  "attacks_used": 8,
  "jumps": 87,
  "forms_collected": "fox,egg",
  "final_form": "egg",
  "max_x_reached": 12400
}

A second notebook cell uses ipywidgets to create a paste-and-save UI. You copy the JSON from the game output, paste it into a text area, click Save, and it appends the row to a game_stats Delta table in the Lakehouse.

A third cell runs a SQL summary:

SELECT
  COUNT(*) AS total_games,
  SUM(CASE WHEN won THEN 1 ELSE 0 END) AS wins,
  ROUND(100.0 * SUM(CASE WHEN won THEN 1 ELSE 0 END) / COUNT(*), 1) AS win_rate,
  MAX(score) AS high_score,
  ROUND(SUM(duration_seconds) / 60, 1) AS total_playtime_min,
  SUM(coins_collected) AS total_coins,
  SUM(enemies_stomped + enemies_zapped) AS total_kills,
  SUM(deaths_total) AS total_deaths,
  SUM(jumps) AS total_jumps
FROM game_stats

Now my kids argue about who is the best player — with data.

Taking It Further: Power BI Report & Automated Stats Collection

The Delta table is nice, but I imagine my boys want a leaderboard they can check anytime — not a SQL query. So the next step is a Power BI report on top of the game_stats table. Direct Lake, obviously. No import, no scheduled refresh. The report reads straight from the Delta table in the Lakehouse. Every new game round shows up the moment you refresh the page.

But the manual paste step still bothers me. Right now, after each game-over you have to copy the JSON, switch to the save cell, paste it, and click Save. That works, but it’s manual. The goal is to fully automate it — the game writes stats directly to the Lakehouse without any manual step in between. The challenge: Fabric’s displayHTML() runs inside a sandboxed iframe with no access to the parent notebook, no postMessage, no clipboard API, and no way to call PySpark from JavaScript. Every automatic approach I tried — OneLake REST API from the iframe, postMessage to the notebook, navigator.clipboard — was blocked by the sandbox.

The workaround I am exploring: a lightweight Fabric API endpoint (or a Data Pipeline trigger) that the game’s JavaScript can POST the JSON to directly. If Fabric eventually opens up notebook cell-to-cell communication or relaxes the iframe sandbox, the whole thing becomes a one-click experience. For now, the ipywidgets paste-and-save UI is the pragmatic solution.

Why This Matters (Beyond the Fun)

This project is a real demonstration of what Fabric can do:

  • displayHTML() renders any self-contained HTML inside a notebook cell — games, dashboards, custom UIs, interactive visualizations
  • ipywidgets enables interactive controls (text areas, buttons, dropdowns) natively in notebook output — no external web framework needed
  • Delta tables are first-class citizens — any structured data, from any source, can land in the Lakehouse
  • SQL analytics run directly against Delta tables — no separate query engine, no ETL
  • The full pipeline — from data generation to storage to analysis — lives in a single notebook

If a kids’ game can become a full analytics pipeline in a weekend, imagine what you can do with your business data.

Try It Yourself

The complete source code is on GitHub:

🔗 https://github.com/KornAlexander/jump-and-run

File Description
jump_and_run python game basis.py Original Python/Pygame version
jump_and_run.html HTML5 Canvas version with stats tracking
jump_and_run_v2.html V2 with audio, particles, parallax & smooth camera
Jump and Run.ipynb Fabric notebook — play, save stats, view summary

To run it in Fabric:

  1. Upload the HTML file to your Lakehouse → Files
  2. Create a notebook attached to the Lakehouse
  3. Run: displayHTML(open("./builtin/jump_and_run.html").read())

(I do not endorse burning your company’s Fabric capacity for gaming. But if your manager asks — you were “testing displayHTML performance.”)

Power BI Report Prototyping with AI: From 35 Pages to 18 in 10 Minutes

Power BI Report Prototyping with Excalidraw and Copilot Agent Mode

How I used GitHub Copilot Agent Mode, Playwright MCP, and Excalidraw to prototype a Power BI report redesign — automatically.

1. The Problem

You have a Power BI report with 35 pages and 5 levels of drill-through navigation. Users get lost. Pages are hard to maintain. You want to redesign the navigation — but how do you plan it visually before touching a single Power BI file?

Traditional approaches involve:

  • Opening the report and clicking through pages manually
  • Sketching boxes on a whiteboard
  • Creating mockups in PowerPoint

None of these give you a connected view of what exists (screenshots of every page) alongside what should change (proposed page hierarchy).

2. The Solution: AI-Powered PBI Prototyping

I built a workflow that combines three tools:

  1. Playwright MCP — automatically screenshots every page of a Power BI report
  2. Excalidraw — organizes screenshots with labels, levels, and before/after comparison diagrams
  3. PBIR files — extracts the actual page navigation graph from the report definition

All orchestrated by GitHub Copilot in Agent mode inside VS Code.

Power BI Report Prototyping Process — automated workflow using Copilot Agent Mode

The complete prototyping workflow — from opening the report to the final Excalidraw file.

3. Screenshot Every Page with Playwright

Power BI renders visuals on <canvas> elements — there’s no way to extract the visual content as DOM elements. Screenshots are the only viable approach.

Using the Playwright MCP server in VS Code, Copilot navigates to the report, opens the page dropdown at the bottom, and loops through every page — clicking each one, waiting for visuals to render, and taking a screenshot. The entire process is hands-free.

Key learnings:

  • Open the report in full-screen mode before running the script — avoids capturing sidebars and filter panes
  • Wait 4 seconds per page for visuals to render (8 seconds for complex pages)
  • For non-default tenants, launch Edge with --remote-debugging-port=9222 and connect Playwright via CDP

Result: 35 clean, full-screen screenshots in under 3 minutes.

4. Extract Page Navigation from PBIR Files

Instead of manually mapping which pages link to which, I export the report’s PBIR definition via the Fabric REST API.

Every Power BI project (PBIP) consists of two parts: the PBIR (report definition) and the TMDL (semantic model definition). The PBIR contains a JSON file for every page and every visual — including the exact drill-through targets, button navigation actions, and page references. This means the complete navigation graph of the report is encoded in these files.

# Authenticate and export the report definition via Fabric REST API
Login-PowerBIServiceAccount
$uri = "https://api.fabric.microsoft.com/v1/workspaces/$workspaceId/reports/$reportId/getDefinition"
$response = Invoke-WebRequest -Uri $uri -Method Post -Headers $headers

# The API returns a long-running operation — poll until complete,
# then save all files locally (pages, visuals, assets, themes, ...)

By searching for cross-page references in the visual.json files, I can extract the complete navigation graph:

From: Page 1 (Overview) → To: Page 2a, Page 2b, Page 2c, ...
From: Page 2a → To: Page 3a, Page 3b, ...
From: Page 3a → To: Page 4a
From: Page 4a → To: Page 5a

This gives me the actual drill-through depth for every page — no guessing required.

Result: A complete navigation graph with hundreds of cross-page links, automatically determining that some sections have 5 levels of drill-through.

5. Build the Excalidraw Prototype

Excalidraw files are pure JSON — no special tools needed. I generate the entire prototype programmatically with Python. Screenshots are embedded as base64-encoded images directly into the Excalidraw JSON file, so the result is a single portable file with everything included.

Color-Coded Level Indicators

Each page gets a label with color coding based on its drill-through depth:

  • L1 (Teal) — Landing page
  • L2 (Blue) — First entry from overview
  • L3 (Purple) — Analysis pages
  • L4 (Orange) — History pages
  • L5 (Red) — Detail drill-through
  • TT (Gray) — Tooltip pages

Layout: Categories Horizontal, Pages Vertical

Categories are arranged as columns. Within each column, pages are stacked vertically in drill-through order. This gives an instant visual sense of which areas are deep and which are flat.

Before/After Comparison Diagrams

Below each category’s screenshots, I add box diagrams showing the current vs. proposed hierarchy:

BEFORE (12 pages, 5 levels)

[L1] Page 1
  └─ [L2] Page 2a
       ├─ [L3] Page 3a
       │    └─ [L4] Page 4a
       │         └─ [L5] Page 5a
       └─ [L3] Page 3b
            ├─ [L4] Page 4b
            └─ [L4] Page 4c

AFTER (5 pages, 3 levels)

[L1] Page 1
  └─ [L2] Page 2a
       ├─ [L3] Page 3a
       │    + Page 4a content (merged as table)
       │    + Page 5a content (merged as drill-through)
       └─ [L3] Page 3b
            + Page 4b content (merged as table)
            + Page 4c content (merged as chart)

Result: 35 pages → 18 pages. 5 levels → 3 levels. Clear visual comparison.

6. A Note on Figma

I also tried the Figma MCP server for this workflow. Figma’s capture can convert HTML pages into editable design layers — great for web UIs. I successfully captured the before/after box diagrams as editable Figma frames.

However, for Power BI prototyping specifically, Figma has limitations: Power BI renders visuals on <canvas> elements which are invisible to the capture script, the MCP is write-only (no editing existing layers), and each capture ID is single-use. If you need to share prototypes with designers already in Figma, it works as an additional output — but Excalidraw is the better primary tool because:

  • Full programmatic control — the JSON file format allows unlimited manipulation
  • No API limits or authentication required
  • Works offline in VS Code with the Excalidraw extension
  • Images, text, shapes, and arrows in a single portable file

7. The Complete Workflow

  1. Open PBI report in browser (Playwright MCP)
  2. Screenshot all pages (loop through page dropdown)
  3. Export PBIR definition (Fabric REST API)
  4. Parse navigation graph from visual.json files
  5. Build Excalidraw file with Python: embed screenshots, add labels, generate comparison diagrams
  6. Review in VS Code with Excalidraw extension
  7. Optionally capture to Figma for sharing

Total time: ~10 minutes for a 35-page report, fully automated.

8. Try It Yourself

Prerequisites

  • VS Code with GitHub Copilot (Agent mode)
  • Playwright MCP server (npx @playwright/mcp@latest)
  • Excalidraw VS Code extension (pomdtr.excalidraw-editor)
  • MicrosoftPowerBIMgmt.Profile PowerShell module

Quick start

  1. Open VS Code with GitHub Copilot (Agent mode)
  2. Install the Playwright MCP server (npx @playwright/mcp@latest)
  3. Open your Power BI report in the browser and copy the URL
  4. Ask Copilot: “Screenshot all pages of this Power BI report, download the PBIR definition, analyze the page navigation hierarchy, and create an Excalidraw file organized by category with level indicators and before/after comparison diagrams”

The AI handles the automation. You focus on the design decisions.

9. Beyond Prototyping: Visual Report Documentation

This workflow isn’t just for redesigning reports. It’s equally valuable as a documentation tool for important production reports:

  • Onboarding new team members — Instead of clicking through a 30-page report live, hand them an Excalidraw file with every page screenshot, labeled with descriptions and navigation hierarchy. They see the full picture in one canvas.
  • Change tracking — Run the screenshot workflow periodically and compare versions side by side. Visual diffs catch layout changes that text-based diffs miss.
  • Stakeholder communication — Share the Excalidraw file (or a Figma capture) with business stakeholders to discuss which pages they actually use, which are redundant, and where gaps exist.
  • Audit and compliance — For regulated environments, having a visual snapshot of every report page with timestamps serves as documentation of what was deployed and when.
  • Cross-team alignment — When multiple teams build reports in the same workspace, the navigation graph reveals overlaps, orphaned pages, and opportunities to consolidate.

The same 10-minute automated process that enables prototyping also produces comprehensive visual documentation — with zero manual effort.

What’s Next

  • Automated PBIR modification — Once the prototype is approved, programmatically update the report’s page structure to match the proposed design
  • Semantic model analysis — Apply the same prototyping approach to data model optimization
  • Template generation — Create reusable prototyping templates for common report patterns

Tools used: GitHub Copilot (Claude), Playwright MCP, Figma MCP, Excalidraw, Microsoft Fabric REST API, PowerShell, Python

Power BI Fixer — One Line to Fix Them All

How a single Python call in a Fabric Notebook can assess, standardize, and fix your Power BI reports and semantic models at scale.

DISCLAIMER: RUN THE SCRIPT AT YOUR OWN RISK. NOT MICROSOFT AFFILIATED.

Try it out → Go here

1. Why This Matters

Every Power BI developer knows the pain. You inherit a report — or revisit one you built six months ago — and find pie charts everywhere, axis labels on bar charts that serve no purpose, the default 720×1280 page size from 2018, visual filters that confuse end users, no calendar table, no “Last Refresh” indicator, and implicit measures galore.

Fixing all of that manually means clicking through dozens of property panes, writing Tabular Editor scripts, exporting TMDL, or opening the JSON files in Power BI Desktop developer mode. And that is just for one report.

What if you could do it all — for any report in any workspace — in a single line?

pbi_fixer()

That is not a concept. That is a working function, built on top of Semantic Link Labs, running natively inside a Microsoft Fabric Notebook. No external tools. No downloads. No separate C# runtime. Just Python, the Fabric APIs, and one function call.

A big thank you to Michael Kovalsky, the creator and maintainer of Semantic Link Labs. His work laid the groundwork that makes the PBI Fixer possible. He also pointed me in the right direction when I was exploring whether an interactive UI was even feasible inside a Fabric Notebook. Without Semantic Link Labs, none of this would exist.

The Game Changer: A Native Fabric Solution

One Line. (Nothing Else.)

The PBI Fixer is a notebook-native interactive UI built with ipywidgets. When you call pbi_fixer(), it renders a complete control panel directly in your notebook cell output:

  • Workspace / Report / Page input fields to target exactly what you need
  • Mode selector — Fix, Scan, or Scan + Fix
  • 11 fixers split across two categories — Report Visuals and Semantic Model
  • Checkbox control — pick exactly the fixers you want
  • XMLA write confirmation — a safety gate for semantic model modifications
  • Progress log — live output showing every action, every finding, every fix

You get the same result whether you point it at a report in your personal workspace or at a production workspace with a service principal. It is the same API, the same tool, the same one line.

PBI Fixer UI in a Fabric Notebook

If You Are Not on Fabric, You Are Missing Out

Let me be direct: if you are still exclusively on Power BI Pro or Premium Per User without a Fabric capacity, you are missing out on an entire generation of tooling. The PBI Fixer leverages:

  • Fabric Notebooks — serverless Python compute with ipywidgets for interactive UIs
  • PBIR format — the enhanced report format that exposes every visual property as JSON, readable and writable through the Fabric REST API
  • XMLA endpoints — read/write access to the Tabular Object Model (TOM) for semantic model modifications
  • Semantic Link Labs — a community-driven Python library (pip install semantic-link-labs) that wraps all of this into clean, documented functions

None of this requires downloading anything to your machine. You open a notebook, run one cell, and you have an interactive report-fixing tool at your fingertips. This is what a modern BI platform looks like — and Fabric delivers with being able to run this community‑driven tool directly integrated. But also let’s be clear again: the Power BI Fixer is obviously not a Microsoft product.

2. What the PBI Fixer Does — In Detail

The tool is split into two categories, each addressing a different layer of your Power BI solution.

Report Fixers (5 Fixers at the time of writing)

These fixers operate on the report definition — the PBIR JSON files that describe every visual, page, and filter in your report. They use the connect_report() context manager to read (and optionally write) the report definition through the Fabric REST API. Some of the following fixes could more easily be applied by adding on single json design theme to the report, but this would not fix any chart where the default has been touched, therefore the following fixes are directly modifying the property of each visual / page.

1. Fix Pie Charts

Problem: Pie charts are one of the worst chart types for comparing values. Human perception of angles and areas is far less accurate than perception of length (bars). Yet pie charts remain one of the most used visuals in Power BI.

What it does: Replaces every pieChart visual in the report with a clusteredBarChart (configurable in the function itself). The data bindings, filters, and layout are preserved — only the visual type changes.

Scan mode: Reports exactly how many pie charts exist and on which pages, without modifying anything.

🔍 [1/5] Scanning Fix Pie Charts…
   🟡 definition/pages/abc123/visuals/def456/visual.json — pie chart found (would be replaced with clusteredBarChart)

2. Fix Bar Charts

Problem: Bar charts are great — but Power BI’s default formatting includes axis titles nobody reads, axis value labels that duplicate the data labels, and gridlines that add visual clutter.

What it does: Applies five best-practice formatting rules to every barChart and clusteredBarChart:

Setting Target Why
X axis title Off The axis title (“Count of Sales”) adds no value — the data labels speak for themselves
X axis values Off Redundant when data labels are shown directly on the bars
Y axis title Off Category names are self-explanatory
Data labels On The single most important formatting choice — show the actual numbers
Vertical gridlines Off Reduces visual noise

Scan mode: Reports which specific properties deviate from the target for each visual, so you know exactly what would change.

🟡 definition/pages/…/visual.json — needs fixing: X axis title, X axis values, Data labels

3. Fix Column Charts

Problem: Same issues as bar charts, but with axes flipped. Power BI’s defaults for column charts include unnecessary axis titles, missing data labels, and gridlines.

What it does: Applies the equivalent five formatting rules to every columnChart and clusteredColumnChart, with the axis semantics appropriately swapped.

4. Fix Page Size

Problem: The default Power BI page size is 720×1280 pixels — a legacy from when Full HD was not universal. Reports created with this size appear small and waste screen real estate on modern displays.

What it does: Upgrades pages from the default 720×1280 to 1080×1920 (Full HD). Pages with custom sizes are left untouched — only the exact default match triggers a fix.

Scan mode: Reports which pages use the default size and which have custom dimensions.

🟡 “Sales Overview” (definition/pages/abc123/page.json) — default page size (720×1280), would be upgraded to 1080×1920
🟢 “Executive Summary” (definition/pages/def456/page.json) — custom size (800×1200), no change needed

5. Hide Visual Filters

Problem: Power BI automatically creates visual-level filters for every field used in a visual. These filters appear in the Filters pane and confuse end users who do not understand the distinction between visual, page, and report filters. Most report developers would rather hide them entirely.

What it does: Sets isHiddenInViewMode = True on every visual-level filter. For visuals that have query fields but no filterConfig, one is constructed automatically — the tool walks the visual’s queryState, classifies each field as Categorical or Advanced (for measures), and creates the hidden filter entries from scratch.

Scan mode: Reports how many visuals have visible filters and how many filters would be hidden.

Semantic Model Fixers (6 Fixers at the time of writing)

These fixers operate on the semantic model (the dataset) behind the report. They use the XMLA endpoint and the Tabular Object Model (TOM) through connect_semantic_model(). Because XMLA write operations are irreversible (the model can no longer be downloaded as a .pbix with embedded data after modification), the UI requires an explicit confirmation checkbox before running these fixers.

1. Discourage Implicit Measures

Problem: By default, Power BI allows end users to drag columns into the Values bucket and get automatic SUM/COUNT/etc. aggregations. This creates “implicit measures” that cannot be formatted, documented, or governed. It is also a prerequisite for calculation groups — they simply do not work correctly with implicit measures enabled. Actually if a calculation group is added this setting is automatically set to TRUE

What it does: Sets DiscourageImplicitMeasures = True on the model. One property, one toggle, impact the whole model.

2. Add Calendar Table (CalcCalendar)

Problem: Almost every analytical model needs a calendar (date) table. Yet many models either lack one entirely or use a poorly structured one. Without a proper calendar table marked with DataCategory = Time, time intelligence functions do not work and auto date/time creates hidden tables for every date column.

What it does: Checks if any table in the model has DataCategory = “Time”. If none exists, creates a comprehensive CalcCalendar calculated table with:

  • 20 columns — Date, Year, Quarter, Month (number and abbreviation), Day, Fiscal Year (October start), End of Month, Week of Year, Weekday, and 9 boolean/flag columns (Is Current Month, Is Previous Month, Is Current/Previous Calendar Year, Is Current/Previous Fiscal Year, Is Before This Month, Is Current or Past Month, Month Key, Relative Month)
  • 3 hierarchies — Date Hierarchy (Year → Quarter → Month → Day), Fiscal Date Hierarchy (Fiscal Year → Quarter → Month → Day), Calendar Hierarchy (Year → Month MMM → Week of Year → Weekday)
  • Display folders — Organized into Favorites, Calendar Date, Fiscal Date, and Flags
  • Sort-by-column — Month abbreviation sorted by month number
  • Marked as date table with the Date column as key

The DAX expression uses CALENDARAUTO() and derives all columns dynamically. The fiscal year start month is configurable (default: October).

3. Add Last Refresh Table

Problem: Users and administrators need to know when a dataset was last refreshed. This is especially critical for business-critical reports where stale data leads to wrong decisions.

What it does: Creates a hidden “Last Refresh” table with:

  • M partition (Power Query) that captures DateTime.LocalNow() on every refresh
  • Data column storing the refresh timestamp
  • Measure “Last Refresh Measure” displaying the formatted timestamp
  • Smart placement — If a “Measure” table exists, the measure is placed there instead of in the Last Refresh table, keeping things organized

4. Add Measure Table

Problem: Without a dedicated measure table, measures are scattered across fact and dimension tables. This makes them hard to find, hard to maintain, and easy to duplicate.

What it does: Creates an empty calculated table named “Measure” using the minimal expression {0} (a single-row, single-column placeholder). The auto-generated column is hidden, so only measures placed in this table are visible to the end user.

5. Add Units Calculation Group (Thousand / Million)

Problem: Displaying large numbers like “1,234,567” clutters visuals and makes comparison harder. Users often want to see “1,235K” or “1.2M”. Creating individual measures for every combination is not scalable.

What it does: Creates a “Units” calculation group with two items:

  • Thousand — divides by 1,000
  • Million — divides by 1,000,000

Both items include smart skip logic: measures whose name contains % or ratio are passed through unchanged — dividing a percentage by 1,000 would produce nonsense. The DAX uses DIVIDE() for safe division and checks ISNUMBER(SELECTEDMEASURE()) before applying.

Note: Calculation groups affect all visuals that use the slicer. The fixer includes a performance warning in its output.

6. Add Time Intelligence Calculation Group (21 Items)

Problem: Time intelligence is the most common analytical requirement — comparing this year to last year, computing year-to-date, calculating variances. Building these for every measure individually is tedious and error-prone. A calculation group solves this once for all measures.

What it does: Creates a “Time Intelligence” calculation group with 21 calculation items:

Category Items
Base periods AC (Actual), Y-1 (Prior Year), Y-2 (Two Years Ago), Y-3 (Three Years Ago)
Year-to-date YTD, YTD-1 (Prior Year YTD), YTD-2 (Two Years Ago YTD)
Absolute variances abs. AC vs Y-1, abs. AC vs Y-2, abs. AC vs YTD-1, abs. AC vs YTD-2
Relative variances AC vs Y-1 (%), AC vs Y-2 (%), AC vs YTD-1 (%), AC vs YTD-2 (%)
Achievement achiev. AC vs Y-1, achiev. AC vs Y-2, achiev. AC vs YTD-1, achiev. AC vs YTD-2

The DAX is generated dynamically based on the actual calendar table and date column names in the model. It uses SAMEPERIODLASTYEAR(), DATEADD(), DATESYTD(), and TOTALYTD() — all parameterized so they work regardless of how your calendar table is named.

Prerequisite: Requires a calendar table with DataCategory = “Time”. The fixer checks for this and prints a helpful message suggesting to run the Calendar fixer first if none exists.

3. Scan vs. Fix — Assess Before You Act

Every fixer in the PBI Fixer supports two modes:

Scan Mode

Scan mode is read-only. It opens the report or semantic model with readonly=True, checks every condition, and reports what would change — without touching anything. This is your assessment tool.

Use it to:

  • Audit a report before handing it to a client
  • Baseline the current state before applying fixes
  • Review across multiple reports to prioritize which ones need the most work

The output is a detailed log with green dots (✅ already correct), yellow dots (⚠️ would be fixed), and clear descriptions of each finding.

Fix Mode

Fix mode opens the report or semantic model with readonly=False and applies all selected fixes. The report definition changes are saved back to the Fabric service through the REST API. Semantic model changes are saved through the XMLA endpoint via TOM.

Scan + Fix

The third mode runs both phases sequentially: first a complete scan to document the current state, then a full fix pass. This gives you a before-and-after view in a single run.

4. How It Compares to Other Solutions

Tabular Editor C# Scripts

Let me be clear: I am a huge fan of Tabular Editor. For example TE’s C# scripting engine is powerful, mature, and battle-tested. Being able to save reusable C# scripts as one-click actions in your toolbar makes repetitive model tasks fast and consistent. If you work with semantic models professionally, Tabular Editor should absolutely be in your toolkit.

That said, there are scenarios where the PBI Fixer complements what Tabular Editor offers:

  • Local install required — Tabular Editor must be downloaded and installed on a Windows machine (or run as a portable version). The PBI Fixer runs entirely in a Fabric Notebook — no installs, no local machine dependency
  • C# vs. Python — Tabular Editor’s scripting language is C#. If your team lives in notebooks and Python, switching to C# for model automation is a context switch. The PBI Fixer keeps everything in one language and one environment
  • Semantic model only — Tabular Editor (understandably) focuses on the semantic model layer. It does not touch the report definition — it cannot fix pie charts, page sizes, or visual filters. The PBI Fixer covers both layers in a single tool
  • Interactive notebook UI — Tabular Editor scripts are run from a script editor, command line, or via Macros in the TE UI. The PBI Fixer provides an interactive widget directly inside a notebook, which fits naturally into Fabric-native workflows
  • License cost — TE3 (with the full scripting engine and Macros) requires a paid license, mho worth it for professional use, but it is a consideration for teams already invested in Fabric

Use Tabular Editor and its Macros for deep semantic model work at your desk. Use the PBI Fixer when you need report-layer fixes, notebook-native automation, or a single entry point that spans both layers without leaving Fabric.

TMDL Scripts / Git-Based Workflows

TMDL (Tabular Model Definition Language) is Microsoft’s text-based serialization format for semantic models. You can version-control your models in Git and apply changes through text manipulation.

  • Great for version control — TMDL excels at tracking changes over time
  • Manual text editing — adding a 20-column calendar table means writing 200+ lines of TMDL by hand
  • No report layer — TMDL is semantic-model-only, same as Tabular Editor
  • No assessment — you cannot “scan” a TMDL folder to find what is missing; you need to read and interpret the files yourself
  • Deployment pipeline required — changes in TMDL need to be deployed back to the service through Git integration or a deployment tool

The PBI Fixer’s scan mode provides the assessment that TMDL workflows lack, and its fix mode applies changes directly without requiring a full deployment pipeline.

Manual Modifications in Power BI Desktop

The most common approach: open the report, click through property panes, change settings, save, publish.

  • Time-consuming — fixing five formatting properties on ten bar charts means 50+ individual clicks
  • Error-prone — easy to miss a visual or forget a setting
  • Not scalable — doing this for 20 reports across five workspaces is a full day of work
  • No audit trail — no record of what was changed or what the previous state was
  • Not repeatable — the next report you create has the same default problems

The PBI Fixer is repeatable, consistent, and logs everything all by design.

Power BI REST API / Fabric SDK (Custom Python)

You could build this yourself using the raw Fabric REST API and the semantic-link SDK.

  • Maximum flexibility — you can do anything the API supports
  • Significant development effort — understanding the PBIR JSON schema, handling authentication, managing error cases, building a UI
  • No community — your custom scripts are yours to maintain

The PBI Fixer is built on top of Semantic Link Labs, which handles the heavy lifting. It uses connect_report() for the PBIR layer and connect_semantic_model() for TOM — both are well-tested, community-maintained abstractions over the raw APIs.

5. Getting Started

Prerequisites

  • A Microsoft Fabric capacity (F2 or higher, or a trial capacity)
  • A Fabric Notebook in your workspace
  • Semantic Link Labs installed – see script below
  • Reports in PBIR format (required for report fixers — check your Preview Features in Power BI Desktop, enable PBIR, save, and re-publish if your report is still in PBIRLegacy). The Fixer will also tell you in case it is not.
  • XMLA read/write enabled at the tenant level (Admin Portal → Tenant settings → Integration settings → “Allow XMLA endpoints and Analyze in Excel with on-premises datasets”). By default this is enabled. Required for semantic model fixers.
  • Large semantic model storage format enabled (Workspace Settings → Power BI → Large dataset storage format → ON). This applies to all datasets in the workspace and sets DefaultPowerBIDataSourceVersion to V3, which is required for any XMLA write operation. Without it, semantic model fixers will fail.

Usage

Two cells — that is all it takes. Install and import takes approx 1 min, the Fixer itself will run within seconds.

# Cell 1: Install and import
%pip install git+https://github.com/KornAlexander/semantic-link-labs.git
from sempy_labs.report import pbi_fixer

# Cell 2: Run
pbi_fixer()

Splitting this into two cells is intentional. The install only needs to run once per session — after that you can skip Cell 1 entirely and just re-run Cell 2. It also keeps things clean: use “Clear Output” on Cell 1 to remove the verbose pip installation log, so only the interactive PBI Fixer UI remains visible in your notebook.

The interactive UI renders in the cell output. Enter your workspace and report name, select the fixers you want, choose your mode, and click Run.

Note: If your report was uploaded as a .pbix file, it may be in PBIRLegacy format — the visual fixers won’t find any charts until you convert it. Open the report in Power BI Desktop, save it, and re-publish to convert to PBIR. Also make sure Large semantic model storage format is enabled in the workspace settings (see prerequisites above) — without it, all semantic model fixers will fail with an XMLA write error.

> Note: The PBI Fixer currently lives in a fork. Once it is part of the official Semantic Link Labs package, this becomes a true one-liner — just %pip install semantic-link-labs followed by pbi_fixer(). I am working toward getting it merged into the main repository, but given the scope of the fixers and the UI layer, this may take some time and may not happen at all.

For automation or scripting without the UI, call any fixer function directly:

from sempy_labs.report import fix_piecharts, fix_barcharts

# Scan only — see what would change
fix_piecharts(report="Sales Dashboard", workspace="Production", scan_only=True)

# Fix — apply the changes
fix_barcharts(report="Sales Dashboard", workspace="Production")

6. The Road Ahead

The PBI Fixer is designed as a framework, not a fixed set of rules. Each fixer is an independent Python function with a consistent interface: report, workspace, scan only. Adding a new fixer is as simple as writing one function and wiring it into the UI.

Currently available fixers:

Report Fixers

  • fix_piecharts — replaces all pie charts with Clustered Bar Charts
  • fix_barcharts — removes axis titles/values, adds data labels, removes gridlines
  • fix_columncharts — removes axis titles/values, adds data labels, removes gridlines
  • fix_page_size — changes default 720×1280 pages to 1080×1920 (Full HD)
  • fix_hide_visual_filters — sets isHiddenInViewMode on all visual-level filters

Semantic Model Fixers

  • fix_discourage_implicit_measures — sets DiscourageImplicitMeasures to True (recommended & required for calc groups)
  • add_calculated_calendar — adds a calculated calendar table if no table has been marked as a date table
  • add_measure_table — adds an empty “Measure” calculated table to centralise measures
  • add_last_refresh_table — adds a “Last Refresh” table with M partition & measure showing refresh timestamp
  • add_calc_group_units — Thousand & Million items, skips % / ratio measures
  • add_calc_group_time_intelligence — AC, Y-1/Y-2/Y-3, YTD, abs/rel/achiev. variances (requires calendar table)

What’s especially exciting is that this is hopefully just the beginning—because the whole fixer is built for easy extension, anyone can contribute new fixers or improvements. As more people get involved, the checklists and best practices incorporated by the PBI Fixer will continue to grow and evolve. This open and extensible approach means the tool can adapt to new challenges and community needs over time.

Here is what I find most exciting about the extensibility:

IBCS Implementation

The International Business Communication Standards (IBCS) define a comprehensive set of rules for business charts: uniform scaling, standardized notation, consistent color coding, and strict chart type selection. Some of the PBI Fixer’s concepts already align with IBCS principles:

  • Replacing pie charts — IBCS explicitly discourages pie charts in favor of bar charts
  • Removing axis clutter — IBCS emphasizes clean, uncluttered visuals with data labels instead of gridlines
  • Standardizing page sizes — consistent canvas dimensions are a prerequisite for standardized layouts

These are early steps, but the architecture makes it straightforward to explore further IBCS-aligned fixers over time — and to potentially integrate with IBCS-focused custom visuals like TRUECHART for scenarios where native chart types reach their limits.

Simplifying the Defaults

Power BI’s default settings are optimized for getting started quickly — not always for production-quality reports. The idea is that the Fixer will potentially be extended by further best practice checks and fixes:

  • VertipaqAnalyzer integration — Semantic Link Labs already includes vertipaq_analyzer(). A future fixer could scan the model for high-cardinality columns, unused columns, and oversized string columns, then recommend or apply optimizations automatically
  • Report BPA integration — Semantic Link Labs already ships run_report_bpa() with 9 built-in rules that analyze the report definition for issues like oversized visuals, missing alt text, and excessive filters. Integrating this directly into the PBI Fixer as a scan-capable fixer would give you a full report-level health check alongside the visual fixes — and for rules with deterministic resolutions, auto-fix them in the same pass
  • Model BPA integration — The library’s comprehensive Model BPA covers semantic model best practices. Future fixers could take BPA findings and apply fixes for every rule that has a deterministic resolution — turning “assessment” into “assessment + auto-fix” in one step

What You Can Build Today

Even without waiting for future releases, the framework is open. You can write your own fixer function today:

def fix_my_custom_rule(report, page_name=None, workspace=None, scan_only=False):
    with connect_report(report=report, workspace=workspace, readonly=scan_only) as rw:
        # Your logic here — iterate visuals, check properties, apply fixes
        pass

Wire it into the report_fixers list in the UI, and it appears as a checkbox alongside the built-in fixers. Same for semantic model fixers — write a function that uses connect_semantic_model() and add it to sm_fixers.

The barrier to entry is intentionally low. If you can write a Python function that reads and modifies a dictionary, you can write a fixer.

Final Thoughts

The PBI Fixer is not just a tool — it is a shift in how we think about report quality. Instead of manually checking and fixing every visual property, every model setting, every best practice after the fact, we can now codify our standards and apply them at scale with a single function call.

Fabric’s notebook environment, combined with the PBIR format and the XMLA endpoint, has created something that was simply not possible two years ago: a unified, programmatic, interactive interface to both the report layer and the semantic model layer of Power BI — running entirely in the browser, with zero local tooling.

If you are building Power BI solutions professionally, this is the direction things are moving. One line. Everything assessed. Everything fixed.

pbi_fixer()