Why PBIP for local development
All started with a customer editing PBIX files back and forth over email, you can guess how that ended. .pbix is a binary blob, no diffs, no reviews. PBIP (the project format of today, not the old PBIX-only flow) is just text files you can version and generate with Copilot. To start I'm assuming your Part 3 model validated, if RLS isn't tested yet, go back, the Bridge screenshots below won't catch a security hole.
SalesReport.pbip/
├── SalesReport.pbiproj
├── definition/ ← points at Part 3 model (DirectLake)
└── report/
├── report.json
├── pages/ ← Copilot generates these
└── visuals/
Scaffold the PBIP project
# VS Code extensions
code --install-extension ms-powerbi.powerbi-pbip
code --install-extension github.copilot
code --install-extension github.copilot-chat
# Convert or init
pbi-tools convert -input "SalesReport.pbix" -output "SalesReport.pbip" -format PBIP
# ...or init fresh and point dataset at Part 3 model ID
Generate pages + visuals with Copilot
// MCP: powerbi.createReportPage(ws, datasetId, pageSpec)
// report/pages/Overview.reportpage.json
{
"name": "Overview", "displayName": "Executive Overview",
"visualContainers": [
{ "name": "kpi-sales", "visualType": "card", "queryRef": "TotalSales" },
{ "name": "kpi-yoy", "visualType": "card", "queryRef": "YoYGrowth" },
{ "name": "bar-category", "visualType": "clusteredBarChart",
"queryRef": "SalesByCategory" },
{ "name": "line-trend", "visualType": "lineChart",
"queryRef": "MonthlyTrend" }
]
}
Repeat for Product, Customer, Store pages. Open in Power BI Desktop to preview, numbers must match Part 3 DAX tests.
Bind data + add interactions
// MCP: powerbi.bindVisual(ws, reportId, "kpi-yoy", {
measure: "YoY Growth %",
conditionalFormatting: [
{ condition: "> 0", color: "#00875A" },
{ condition: "< 0", color: "#DE350B" }
]})
// MCP: powerbi.addDrillthrough(ws, reportId, "bar-category", "Product")
Interactions live. Click a category bar to trend + KPIs filter. Right-click to drill to Product.
Live preview with Desktop Bridge: reload + screenshot
Editing PBIP JSON in VS Code is only half the loop, you need to see it. The Power BI Desktop Bridge (Preview) lets Copilot and external tools talk to a running Power BI Desktop on your machine, so you never restart to preview.
pbi-desktop-bridge-{processId}. Local-only, one operation at a time. Prereq: Desktop on Windows + File to Options to Preview Features to Enable external tool access to Power BI Desktop through secure local APIs (on by default). See official Bridge overview.
Bridge CLI cheat sheet
# Install once
npm install -g @microsoft/powerbi-desktop-bridge-cli
powerbi-desktop status # which Desktop window / file / unsaved changes / pages
powerbi-desktop manifest # which bridge methods this Desktop version supports
powerbi-desktop open ./SalesReport.pbip # open PBIP and wait for bridge
powerbi-desktop reload # reload PBIR from disk after VS Code edits
powerbi-desktop screenshot Overview # capture one page as PNG
powerbi-desktop screenshot-all # capture every page in pages.json
The Copilot edit to reload to screenshot loop
# 1. check state first (avoid clobbering unsaved work)
powerbi-desktop status
# currentFilePath: .../SalesReport.pbip, hasUnsavedChanges: false [ok]
# 2. reload report from disk (model too by default)
powerbi-desktop reload
# 3. visual check
powerbi-desktop screenshot Overview
# to Overview.png, agent "sees" card positions, flags overlap
No restart. If the screenshot shows overlap, Copilot fixes the JSON and repeats, the same loop the Report Authoring Skill uses.
pageId from PBIR (don't forget that, you get MethodNotFound otherwise). If a reload is already running, wait. Concurrent calls just error out.
How it maps to this series: Fabric MCP = cloud data APIs. Power BI Modeling MCP = model editing. Bridge = controlling the local Desktop renderer. Use all three in Part 4, then reuse screenshot-all in Part 5 for themed before/after proof.
Deploy with GitHub Actions, this is where you need a service principal
Local work used your user login. GitHub Actions can't click "Sign in", so here, and only here in this series, create a service principal: Entra app registration to secret to enable Service principals can use Fabric APIs in Admin portal to add SP to workspace as Admin/Member to store AZURE_CLIENT_ID / SECRET / TENANT_ID as GitHub secrets.
# .github/workflows/deploy-pbip.yml (simplified)
name: Deploy PBIP
on:
push: { branches: [main] }
jobs:
deploy:
runs-on: ubuntu-latest
environment: test
steps:
- uses: actions/checkout@v4
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy PBIP
uses: microsoft/powerbi-deploy-action@v1
with:
pbip-path: 'SalesReport.pbip'
workspace-id: ${{ secrets.PBI_WORKSPACE_ID }}
Merge to main to Test workspace updates. Prod stays manual approval, we'll harden that in the finale's checklist.
What's next in Part 5, the payoff
You have a correct report. Now make it beautiful. Part 5 connects Figma MCP, pulls your design system tokens, generates a Power BI theme, restyles every page, and publishes the stunning finale.