All posts

How to Update a PowerBI App Through the API in Microsoft Fabric

A few weeks back, I hit a frustrating problem while shipping a Fabric project. I set up Fabric Deployment Pipelines, Microsoft’s next-gen…

ChatGPT is getting better at these, damn! PS don’t blame me, I’m an engineer, not a designer!

ChatGPT is getting better at these, damn! PS don’t blame me, I’m an engineer, not a designer!

A few weeks back, I hit a frustrating problem while shipping a Fabric project. I set up Fabric Deployment Pipelines, Microsoft’s next-gen pipelines that can push more item types than classic Power BI deployment pipelines. It all felt familiar and modern… until I tried to trigger the pipeline using the API and realised one critical piece was missing: updating the Power BI app. That capability exists in the new UI and in Power BI deployment pipelines, but not (yet) in Fabric Deployment Pipelines. And because a workspace can only belong to one pipeline at a time, you can’t have both. Lovely.

In this post, I’ll share the not-pretty-but-works workaround I’m using to get fully automated deployments and app updates, until Microsoft adds Fabric pipelines native support for app updates.

The workaround

To restate the problem: Fabric Deployment Pipelines can’t (yet) update a Power BI app when you trigger them through the API. Power BI Deployment Pipelines can, but a workspace can only sit in one pipeline at a time, so either a Fabric or a PowerBI deployment pipeline.

The trick is to temporarily move your Dev and Prod workspaces into a short-lived PowerBI deployment pipeline, run Deploy all with update app enabled, then put everything back in your original Fabric pipeline.

At a high level:

  1. Create a temporary Power BI deployment pipeline

  2. Find your Dev and Prod workspaces by name

  3. Unassign them from the Fabric pipeline

  4. Assign them to the temporary Power BI pipeline

  5. Deploy all(Dev → Prod) with app update

  6. Unassign from Power BI and reassign to Fabric deployment pipeline

  7. Delete the temporary pipeline

It’s not pretty, but it’s safe, automated, and reliable until Microsoft wires app updates into Fabric’s pipeline API. Let’s look at the PowerShell that does this.

Prerequisites & assumptions

Before you run this flow, make sure you have:

  • A bearer token that works for your tenant against both Fabric and Power BI APIs (the script accepts it as input). If you prefer SPN login or MSAL, swap out the token section. The rest stays the same.

  • Dev and Prod workspaces already created and compatible (capacity and region expectations still apply).

  • A principal (user or service principal) that can:
    • read Fabric workspaces
    • manage Power BI pipelines
    • deploy pipeline content

Tutorial: implementing the flow step by step

How I’d like to approach this is with some semi-pseudo code first. After explaining the steps, I’m showing a full working script that has

1) Inputs & auth

Pass in:

  • BearerToken

  • DevWorkspaceName, ProdWorkspaceName

  • Optional DeploymentNote

2) Find your workspaces (Fabric API)

We query GET /workspaces once and filter by displayName to grab the Dev/Prod IDs.

# 1) Resolve workspaces by name (Fabric)
$workspaces = Invoke-Json GET "$FabricBase/workspaces"
$devWs  = $workspaces.value | Where-Object { $_.displayName -eq $DevWorkspaceName }
$prodWs = $workspaces.value | Where-Object { $_.displayName -eq $ProdWorkspaceName }

if (-not $devWs -or -not $prodWs) { throw "Workspace not found. Dev='$DevWorkspaceName' Prod='$ProdWorkspaceName'." }
$devId  = $devWs.id
$prodId = $prodWs.id

Write-Host "Dev:  $DevWorkspaceName ($devId)"
Write-Host "Prod: $ProdWorkspaceName ($prodId)"

3) Snapshot original assignments (Power BI API)

We list Power BI pipelines and their stages, recording where Dev/Prod were assigned (pipelineId + stage order). We’ll use this to restore afterward.

$orig = @{
  DevPipelineId  = $null; DevStageOrder  = $null;
  ProdPipelineId = $null; ProdStageOrder = $null
}

$pipelines = (Invoke-Json GET "$PbiBase/pipelines").value
foreach ($p in $pipelines) {
  $stages = (Invoke-Json GET "$PbiBase/pipelines/$($p.id)/stages").value
  foreach ($s in $stages) {
    if ($s.workspaceId -eq $devId)  { $orig.DevPipelineId  = $p.id; $orig.DevStageOrder  = $s.order }
    if ($s.workspaceId -eq $prodId) { $orig.ProdPipelineId = $p.id; $orig.ProdStageOrder = $s.order }
  }
}

4) Create a temporary pipeline

We POST /pipelines with a timestamped name. This gives us a clean, short-lived home to perform the app update.

$stamp = Get-Date -Format "yyyyMMdd-HHmmss"
$tempName = "temp-app-update-$stamp"
$temp = Invoke-Json POST "$PbiBase/pipelines" @{ displayName = $tempName; description = "Temporary pipeline for app update" }
$tempId = $temp.id
Write-Host "Created temporary pipeline: $tempName ($tempId)"

5) Unassign from current pipeline(s)

If Dev or Prod were already in a pipeline, we POST /pipelines/{id}/stages/{order}/unassignWorkspace so we can reassign them to our temporary pipeline.

6) Assign to the temporary pipeline (Dev=0, Prod=1)

We POST /pipelines/{id}/stages/{order}/assignWorkspace twice: Dev → stage 0, Prod → stage 1.

$deployBody = @{
  sourceStageOrder = $DevOrder
    note = $DeploymentNote
    options = @{ allowCreateArtifact = $true; allowOverwriteArtifact = $true }
    updateAppSettings = @{ updateAdppInTargetWorkspace = $true }
  }
  $deploy = Invoke-Json POST "$PbiBase/pipelines/$tempId/deployAll" $deployBody
  $opId = $deploy.id
  Write-Host "Deploy operation: $opId"

7) Deploy with app update

We POST /pipelines/{id}/deployAll with:

  • sourceStageOrder = 0

  • options.allowCreateArtifact = true

  • options.allowOverwriteArtifact = true

  • updateAppSettings.updateAdppInTargetWorkspace = true

  • We then poll /operations/{operationId} until it completes.

# Poll until finished
  do {
    Start-Sleep -Seconds 10
    $op = Invoke-Json GET "$PbiBase/pipelines/$tempId/operations/$opId"
    Write-Host "Status: $($op.status)"
  } while ($op.status -in @('NotStarted','Executing'))

  if ($op.status -ne 'Succeeded') { throw "Deployment failed: $($op.status)" }
  Write-Host "App update deployment succeeded."

8) Restore original assignments

If Dev/Prod were part of a pipeline before, we simply reassign them back to those original pipeline/stage pairs.

if ($orig.DevPipelineId)  { Assign-Workspace $orig.DevPipelineId  $orig.DevStageOrder  $devId  }
if ($orig.ProdPipelineId) { Assign-Workspace $orig.ProdPipelineId $orig.ProdStageOrder $prodId }

9) Delete the temporary pipeline

We DELETE /pipeline/{id} so nothing lingers.

Invoke-Json DELETE "$PbiBase/pipelines/$tempId" | Out-Null
Write-Host "Temporary pipeline deleted."

Why this is worth it

This workaround keeps Fabric Deployment Pipelines as your “real” deployment mechanism, but uses Power BI deployment pipelines briefly for the one thing Fabric’s pipeline API cannot do yet: updating the app.

You end up with:

  • fully automated deployments

  • consistent app updates

  • a clean rollback to the original pipeline configuration

  • no long-lived extra pipelines to manage

Until Microsoft closes the gap, this is a reliable way to keep your delivery pipeline truly end-to-end.

Hi, I’m Bastiaan 👋🏼 Founder of datalyft, a small Dutch company helping companies transform their raw data into real value. I write about the Modern Data Workflow, where I explore tools & processes to supercharge your data capabilities. Follow me for more!