
Fabric Cli
- 36 installs
- 836 repo stars
- Updated July 29, 2026
- data-goblin/power-bi-agentic-development
Use the Fabric CLI (fab) to manage Microsoft Fabric and Power BI Service workspaces, items, deployments, permissions, and configuration.
About
Expert guidance for the Fabric CLI (fab) to programmatically manage Fabric and Power BI Service workspaces, items, tenants, and deployments in the cloud. A developer uses it to publish, download, discover, or configure workspace items across the Power BI service.
- Manages Fabric/Power BI cloud workspaces and items via fab
- Covers deployment, permissions, and cross-workspace item discovery
Fabric Cli by the numbers
- 36 all-time installs (skills.sh)
- Ranked #1,042 of 2,064 Data Science & ML skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/data-goblin/power-bi-agentic-development --skill fabric-cliAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 36 |
|---|---|
| repo stars | ★ 836 |
| Last updated | July 29, 2026 |
| Repository | data-goblin/power-bi-agentic-development ↗ |
What it does
Use the Fabric CLI (fab) to manage Microsoft Fabric and Power BI Service workspaces, items, deployments, permissions, and configuration.
Files
Fabric CLI
Guidance for using fab to programmatically manage Fabric & Power BI service
- Install via
uv tool install ms-fabric-cli(getuvviawinget install uvorbrew install uv) - Fabric CLI is for working with the Cloud environment and not local files; it works with Power BI Pro, PPU, or Fabric; you DO NOT need a Fabric SKU to use the Fabric CLI
- Keep
fabcurrent: check the installed version against the latestms-fabric-clirelease and upgrade withuv tool upgrade ms-fabric-cliunless the user has pinned a specific version. Discover commands and flags withfab --helpandfab <command> --helprather than hard-coding behavior; the CLI surface changes regularly
[!IMPORTANT]
Any time you encounter errors, user preferences or learnings when using the Fabric cli, ALWAYS note these down in the user memory rules, i.e. .claude/rules/fabric-cli.md for future improvement.This is ONLY for generic learnings and not for item- or task-specific learnings.
When to use this skill
- Use whenever the user mentions "Fabric" or "Power BI"
- Use when user asks about Power BI workspaces, deployment, tenants, publishing, download, permissions, or data
Critical general rules
- IMPORTANT: The first time you use
fabrun check that it is up to date to the latest version (upgrade withuv tool upgrade ms-fabric-cliunless the user has pinned a version) and runfab auth status; If user isn't authenticated, ask them to runfab auth login - Always use
fab --helpandfab <command> --helpthe first time you use a command to understand its syntax - You must search the skill /references/ for relevant reference files that explain certain commands, examples, scripts, or workflows before you start using
fab - Before first use, ask the user if they have Fabric admin access, sensitivity labels or DLP policies, any API restrictions, or preferences for Fabric/Power BI API usage; remind user to add this to memory files
- If workspace or item name is unclear, ask the user first, then verify with
fab lsorfab existsbefore proceeding - Ensure that you avoid removing or moving items, workspaces, or definitions, or changing properties without explicit user direction
- If a command is blocked in your permissions and you try to use it, stop and ask the user for clarification; never try to circumvent it
- Create output directories before export:
fab exportdoes not create intermediate directories;mkdir -pthe output path first or the command fails with[InvalidPath]
Use -f (force) for non-interactive use
The fab CLI prompts for confirmation, so you you must always append `-f` to prevent this UNLESS sensitivity labels are enabled, in which case you must ask the user. Do this for the commands:
fab get -q "definition"; sensitivity label confirmationfab export; sensitivity label confirmationfab import; overwrite confirmationfab cp/fab cp -r; overwrite and sensitivity label confirmationfab rm; delete confirmationfab assign/fab unassign; capacity/domain assignment confirmationfab mv; rename/move confirmation
Quickstart guide
You must read and understand the common list of operations with simple examples
0. Check the commands, syntax, and auth status: fab --help and fab auth status 1. Check if the item exists if the user gave the workspace and item name: fab exists "spaceparts-dev.Workspace/spaceparts-otc-full.SemanticModel" 2. Find an item by name across every workspace the user can see: fab find 'sales' -P type=Report -l (substring on name, description, workspace; -P type= to filter, -l for ids; -q '<jmespath>' for client-side filter/projection). For governance workflows that need last visit / last refresh / owner / storage mode / capacity SKU, use `scripts/search_across_workspaces.py`; see workspaces.md for the delta. 3. Find the workspace: fab ls 4. Find the item: fab ls "Workspace Name.Workspace" 4. Check the commands for that item:
fab descto get itemTypesfab desc .<ItemType>for commands i.e.fab desc .SemanticModel
5. What's in that item; what's it for; what is it?:
- Full TMDL definition:
fab get "spaceparts-dev.Workspace/spaceparts-otc-full.SemanticModel" -q "definition" -f - Search a specific measure / table / column:
fab get "ws.Workspace/Model.SemanticModel" -q "definition" -f | rga -i "Sales Amount"
6. Get files, tables, or table schemas:
- List lakehouse files:
fab ls "ws.Workspace/LH.Lakehouse/Files" - List lakehouse tables:
fab ls "ws.Workspace/LH.Lakehouse/Tables" - Table schema:
fab table schema "ws.Workspace/LH.Lakehouse/Tables/gold/orders"
7. Query data (always prefer the wrapper scripts over raw fab api / duckdb / sqlcmd; they resolve IDs, hosts, and auth for you):
- Semantic model (DAX):
python3 scripts/execute_dax.py "ws.Workspace/Model.SemanticModel" -q "EVALUATE TOPN(10, 'Orders')" - Lakehouse or warehouse (DuckDB + Delta against OneLake):
python3 scripts/query_lakehouse_duckdb.py "ws.Workspace/LH.Lakehouse" -q "SELECT * FROM tbl LIMIT 10" -t gold.orders - Lakehouse SQL endpoint, warehouse, or SQL database (T-SQL via
sqlcmd+azsession):python3 scripts/query_sql_endpoint.py "ws.Workspace/LH.Lakehouse" -q "SELECT TOP 10 * FROM dbo.orders"
8. Set properties for an item or workspace: fab set "ws.Workspace/Item.Notebook" -q displayName -i "New Name" or fab set "ws.Workspace" -q description -i "Production environment" 9. Review or manage permissions:
- Item ACL:
fab acl ls "ws.Workspace/Model.SemanticModel"thenfab acl set "ws.Workspace/Model.SemanticModel" -I user@contoso.com -R Read - Workspace roles:
fab acl ls "ws.Workspace"thenfab acl set "ws.Workspace" -I user@contoso.com -R Member
10. Deploy items to Fabric: fab import "ws.Workspace/New.Notebook" -i ./local-path/Nb.Notebook -f 11. Download items from Fabric: fab export "ws.Workspace/Nb.Notebook" -o ./backup -f (always mkdir -p ./backup first) 12. Copy or move items between workspaces: fab cp "dev.Workspace/Item.Notebook" "prod.Workspace" -f or fab mv "ws.Workspace/Old.Notebook" "ws.Workspace/New.Notebook" -f 13. Open item in Fabric via browser: fab open "spaceparts-dev.SpaceParts/Amazing Report.Report" 14. Using Fabric or Power BI APIs: fab api -A powerbi "groups/<ws-id>/datasets/<model-id>/refreshes" -X post -i '{"type":"Full"}' or fab api "workspaces/<ws-id>/items" 15. Using Azure CLI (advanced) when Fabric CLI doesn't suffice:
- T-SQL over any SQL-capable item ; use `scripts/query_sql_endpoint.py` (reuses
az loginviaActiveDirectoryAzCli; full walkthrough in querying-data.md) - Pass a Key Vault secret to a consumer without ever reading, echoing, or persisting it:
az login --service-principal -u <appId> -t <tenantId> --password "$(az keyvault secret show --vault-name <vault> --name <secret> --query value -o tsv)"; command substitution pipes the secret directly into the child process arg list, never stdout, a file, or a named shell variable - Full fab-vs-az decision matrix: fab-vs-az-cli.md
Essential Concepts
For information about any concepts related to Power BI or Fabric you must search or fetch via the microsoft-learn MCP server (or the pbi-search CLI as an alternative) and ask the user questions with the AskUserQuestion tool; NEVER guess or make assumptions.
Workspaces
- Workspaces are containers for items like Notebooks (and other ETL items), Lakehouses (and other data items), SemanticModels, Reports (and other consumption items), and OrgApps.
- Workspaces can be assigned to different things:
- Deployment Pipelines for lifecycle management (Dev, Test, Prod, etc.)
- Domains for governance and tenant structuring
- Capacities for licensing and resources (Fabric or Premium capacities only; PPU and Pro work differently)
- Git repositories for Source Control via Git integration
Key Patterns
Pay special attention to each of the following areas when using the Fabric CLI
Path Format
Fabric uses filesystem-like paths with type extensions:
"WorkspaceName.Workspace/ItemName.ItemType"
You must quote paths with spaces and punctuation:
"Workspace Name.Workspace/Semantic Model Name.SemanticModel"
For lakehouses this is extended into files and tables:
WorkspaceName.Workspace/LakehouseName.Lakehouse/Files/FileName.extension or /WorkspaceName.Workspace/LakehouseName.Lakehouse/Tables/TableName
For Fabric capacities you have to use fab ls .capacities
Examples:
"Production Workspace.Workspace/Sales Report.Report"Data.Workspace/MainLH.Lakehouse/Files/data.csvData.Workspace/MainLH.Lakehouse/Tables/dbo/customers
Common Item Types
.Workspace- Workspaces.SemanticModel- Power BI datasets.Report- Power BI reports.Notebook- Fabric notebooks.DataPipeline- Data pipelines.Lakehouse/.Warehouse/.SQLDatabase- Data artifacts.SparkJobDefinition- Spark jobs.AISkill- Fabric Data Agents.MirroredDatabase/.MirroredWarehouse- Mirrored databases.Environment- Spark environments.UserDataFunction- User data functions
Full list: You must use fab desc or fab desc .<ItemType> to check syntax and types if the user asks about an item type not listed above.
JMESPath Queries
Filter and transform JSON responses with -q:
# Get single field
-q "id"
-q "displayName"
# Get nested field
-q "properties.sqlEndpointProperties"
-q "definition.parts[0]"
# Filter arrays
-q "value[?type=='Lakehouse']"
-q "value[?contains(name, 'prod')]"
# Get first element
-q "value[0]"
-q "definition.parts[?path=='model.tmdl'] | [0]"Using fab api
fab has an api escape hatch that lets you use any API even if it doesn't have primary commands.
Variable Extraction Pattern
To use fab api you need item IDs. Extract them like this:
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Then use in API calls
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'Admin APIs (Requires Admin Role)
Don't use admin commands or APIs if the user doesn't have Admin access. Here's some examples:
# Find semantic models by name (cross-workspace)
fab api "admin/items" -P "type=SemanticModel" -q "itemEntities[?contains(name, 'Sales')]"
# Find all notebooks
fab api "admin/items" -P "type=Notebook" -q "itemEntities[].{name:name,workspace:workspaceId}"
# Find all lakehouses
fab api "admin/items" -P "type=Lakehouse"
# Common types: SemanticModel, Report, Notebook, Lakehouse, Warehouse, DataPipeline, OntologyFor full admin API reference (cross-workspace discovery, tenant settings read/update, capacity/domain/workspace overrides, activity events): admin.md
Error Handling & Debugging
# Show response headers
fab api workspaces --show_headers
# Verbose output
fab get "Production.Workspace/Item" -v
# Save responses for debugging
fab api workspaces -o /tmp/workspaces.jsonCommon workflows
These are the most common workflows you'll encounter in Fabric
Finding or exploring workspaces, items, or metadata
| Command | Purpose | Example |
|---|---|---|
fab ls | List workspaces / items | fab ls "Sales.Workspace" -l |
fab exists | Check if a path exists | fab exists "Sales.Workspace/Model.SemanticModel" |
fab get | Get item details | fab get "Sales.Workspace" -q "id" |
fab desc | Supported commands per type | fab desc .SemanticModel |
Flags:
-l(long listing)-a(show hidden items)-q(JMESPath filter)-v(verbose output)-o(save response to file)
Fabric discovery follows a drill-down pattern:
- Browsing:
- List workspaces:
fab ls - List items in a workspace:
fab ls "ws.Workspace" -l - Confirm a path exists:
fab exists "ws.Workspace/Item" - Check what commands an item type supports:
fab desc .<ItemType> - Inspection:
- Get item details:
fab get "ws.Workspace/Item" - Pull a single field:
fab get "ws.Workspace" -q "id" - Cross-workspace search:
- Routine search across name, description, workspace:
fab find '<text>' -P type=<Type> -l - Governance fields not in
fab find(last visit, last refresh, owner, storage mode, capacity SKU, Copilot readiness): `scripts/search_across_workspaces.py`; see workspaces.md for the delta - Downstream reports for a given model: `scripts/get-downstream-reports.py`
- Tenant-wide admin APIs: admin.md
Check references before exploring:
- workspaces.md
- folders.md
- admin.md
- reference.md
Querying data
| Command | Purpose | Example |
|---|---|---|
fab get -q "definition" | Get model schema | fab get "ws.Workspace/Model.SemanticModel" -q "definition" -f |
fab api -A powerbi | Execute DAX | fab api -A powerbi "groups/<ws-id>/datasets/<model-id>/executeQueries" -X post -i '{"queries":[{"query":"EVALUATE..."}]}' |
fab ls | Browse files / tables | fab ls "ws.Workspace/LH.Lakehouse/Files" |
fab table schema | Lakehouse table schema | fab table schema "ws.Workspace/LH.Lakehouse/Tables/sales" |
fab cp | Upload / download OneLake file | fab cp ./local.csv "ws.Workspace/LH.Lakehouse/Files/" |
duckdb + delta_scan | Query Delta tables (requires DuckDB) | duckdb -c "... delta_scan('abfss://<ws-id>@onelake.../<lh-id>/Tables/schema/table')" |
duckdb + read_csv/json | Query raw files (requires DuckDB) | duckdb -c "... read_csv('abfss://.../Files/data.csv')" |
Flags:
-A fabric|powerbi|storage|azure(API audience)-X get|post|put|delete|patch(HTTP method)-i(JSON body or file)-f(skip sensitivity prompt on definition pulls).
Fabric exposes three query paths depending on the source; always prefer the wrapper scripts — they resolve IDs, hosts, and auth for you:
- Semantic models (DAX):
- Find model fields first:
fab get "ws.Workspace/Model.SemanticModel" -q "definition" - Query: `scripts/execute_dax.py`
- Lakehouses / Warehouses via Delta over OneLake (DuckDB):
- Query a single table: `scripts/query_lakehouse_duckdb.py` (use
tblas a placeholder and pass-t schema.table) - Multi-table joins or raw files in
Files/: pass--sqlwith your owndelta_scan()/read_csv/read_json_autocalls - Optionally scaffold a Direct Lake model instead: `scripts/create_direct_lake_model.py`
- Lakehouse SQL endpoint, Warehouse, or SQL Database (T-SQL via
sqlcmd): - Query any SQL-capable item: `scripts/query_sql_endpoint.py` (auto-detects host per item type, reuses
az loginviaActiveDirectoryAzCli) - Prefer this over DuckDB when you need
INFORMATION_SCHEMA,sys.*metadata, CTEs, or window functions
Check references before writing queries:
- querying-data.md
- semantic-models.md
- lakehouses.md
- warehouses.md
- sql-databases.md
Changing metadata or access (descriptions, tags, endorsement, properties, bindings, permissions)
| Command | Purpose | Example |
|---|---|---|
fab set | Update property | fab set "ws.Workspace/Item" -q displayName -i "New Name" |
fab mv | Rename / move item | fab mv "ws/Old.Notebook" "ws/New.Notebook" -f |
fab acl ls | List permissions | fab acl ls "ws.Workspace" |
fab acl set | Grant permission | fab acl set "ws.Workspace" -I <objectId> -R Member |
fab acl rm | Revoke permission | fab acl rm "ws.Workspace" -I <upn> |
fab label set | Set sensitivity label | fab label set "ws/Nb.Notebook" --name Confidential |
Flags:
-q <field>+-i <value>(set a single property)-I(object ID or UPN forfab acl)-R Admin|Member|Contributor|Viewer(role forfab acl set)-f(skip confirmation; ask user first if sensitivity labels are in play)
Metadata and access changes fall into a few groups:
- Properties (displayName, description, sensitivity config):
- Native update:
fab set "<path>" -q <field> -i "<value>" - Capture current state first so you can revert:
fab get -v -o /tmp/before.json - Endorsement, certification, and tags (no first-class
fabcommands): - Patch via
fab apiwith item-specific endpoints - Tag workflow: tags.md
- Endorsement patterns: reference.md
- Folder placement:
- Move items between workspace subfolders: folders.md
- Access control and sensitivity labels:
- Grant / revoke:
fab acl set,fab acl rm - Set sensitivity label:
fab label set - Verify the principal first:
az ad user show - Never change permissions or labels without explicit user confirmation
- Bindings:
- Rebind a thin
.Reportto a different.SemanticModel: reports.md - Semantic model source rebinds (e.g. swap a lakehouse): semantic-models.md
Check references before changing metadata:
- reference.md
- tags.md
- folders.md
- reports.md
- semantic-models.md
Working with workspaces
| Command | Purpose | Example |
|---|---|---|
fab mkdir | Create workspace / item | fab mkdir "New.Workspace" -P capacityname=MyCapacity |
fab assign | Attach capacity / domain | fab assign .capacities/cap.Capacity -W ws.Workspace -f |
fab unassign | Detach capacity / domain | fab unassign .capacities/cap.Capacity -W ws.Workspace |
fab start / fab stop | Resume / pause capacity | fab start .capacities/cap.Capacity |
fab cp -r | Fork workspace | fab cp "dev.Workspace" "prod.Workspace" -r -f |
fab rm | Soft-delete (see recovery) | fab rm "ws/Item.Type" -f |
Flags:
-P key=value(creation params forfab mkdir)-W(target workspace forfab assign/fab unassign)-r(recursive copy/move)-bpc(block on path collision forfab cp)-f(skip confirmation)
Workspace-scope operations fall into a few groups:
- Create and provision:
- Create workspace:
fab mkdir "<Name>.Workspace" -P capacityname=<cap> - Attach capacity or domain:
fab assign .capacities/<cap>.Capacity -W <ws>.Workspace - Planning context, create/get/set surface, large storage format, Spark pools, OneLake defaults, Git: workspaces.md
- Copy, fork, download:
- Duplicate a workspace in-tenant:
fab cp -r "dev.Workspace" "prod.Workspace" - Dry-run the source tree first:
fab ls "dev.Workspace" - Full local snapshot (items + lakehouse files): `scripts/download_workspace.py`
- Permissions:
- Inspect / grant / revoke:
fab acl ls | set | rm - Tenant-wide governance audit: use the
audit-tenant-settingsskill from thefabric-adminplugin - Connections and gateways (bound to, but outside, the workspace):
- Credential types (WorkspaceIdentity, SPN, Basic), OAuth2 limits: connections.md
- Datasource binding, credential rotation: gateways.md
- Folders inside a workspace:
- Layout, nesting, conventions: folders.md
Check references before modifying workspaces:
- workspaces.md
- folders.md
- connections.md
- gateways.md
Executing or scheduling jobs (notebooks, notebook cells, pipelines, semantic model refresh)
| Command | Purpose | Example |
|---|---|---|
fab job run | Run synchronously | fab job run "ws/ETL.Notebook" -P date:string=2025-01-01 |
fab job start | Run asynchronously | fab job start "ws/ETL.Notebook" |
fab job run-list | List executions | fab job run-list "ws/Nb.Notebook" |
fab job run-status | Check status | fab job run-status "ws/Nb.Notebook" --id <job-id> |
fab job run-cancel | Cancel a job | fab job run-cancel "ws/Nb.Notebook" --id <job-id> -w |
fab api -A powerbi .../refreshes | Trigger semantic model refresh | fab api -A powerbi "groups/<ws-id>/datasets/<model-id>/refreshes" -X post -i '{"type":"Full"}' |
Flags:
-P key:type=value(parameters, type isstring|int|bool)--id(job run ID)-w(wait on cancel)--timeout(overall timeout for synchronous runs)--polling_interval(status poll cadence)
Jobs map to different endpoints depending on item type:
- Notebooks and pipelines:
- Run synchronously:
fab job run "ws/ETL.Notebook" -P date:string=2025-01-01 - Run asynchronously:
fab job start "ws/ETL.Notebook" - Check status:
fab job run-status "ws/Nb.Notebook" --id <job-id> - List history:
fab job run-list "ws/Nb.Notebook" - Python / PySpark kernels, Livy sessions, cell-level CRUD: notebooks.md
- Semantic model refresh (not exposed as
fab job): - Trigger:
fab api -A powerbi "groups/<ws-id>/datasets/<model-id>/refreshes" -X post -i '{"type":"Full"}' - Check current run before starting a new one (409 if already running):
fab api -A powerbi "groups/<ws-id>/datasets/<model-id>/refreshes?\$top=1" - Enhanced refresh, incremental policies, partition targeting: semantic-models.md
- Dataflow refresh:
- Gen1 and Gen2 have different endpoints: dataflows.md
- Scheduling:
- Per-item schedules via the scheduler API: notebooks.md, reference.md
Check references before running jobs:
- notebooks.md
- semantic-models.md
- dataflows.md
- reference.md
Fabric admin operations (auditing, management)
| Command | Purpose | Example |
|---|---|---|
fab api "admin/items" | Cross-workspace item search | fab api "admin/items" -P "type=SemanticModel" -q "itemEntities[?contains(name,'Sales')]" |
fab api "admin/workspaces" | Workspace inventory | fab api "admin/workspaces" |
fab api "admin/tenantsettings" | Tenant settings | fab api "admin/tenantsettings" |
fab api "admin/capacities" | Capacity inventory | fab api "admin/capacities" |
fab api -X post .../update | Update tenant setting | fab api -X post "admin/tenantsettings/<name>/update" -i body.json |
Flags:
-P key=value(query params, e.g.type=SemanticModel)-q(JMESPath filter)-X post+-i(write ops)--show_headers(inspectRetry-Afteron 429)
Admin-scope work is gated behind the Fabric / Power BI admin role. Confirm access first with fab api "admin/capacities" 2>&1 | head -5; if it errors, stop rather than retry.
Two entry points cover most admin tasks:
- Governance audits (tenant settings, delegated overrides, Entra SG scoping):
- Use the
audit-tenant-settingsskill from thefabric-adminplugin. It owns the curated metadata baseline, the audit + change-detection script, delegated-override enumeration, and the Entra SG investigation workflow. - Invoke it whenever the question combines tenant posture with group membership, override scope, or drift against the baseline.
- Raw admin APIs (cross-workspace search, activity events, artifact access, item search):
- Patterns in admin.md
- Rate limit: 25 write requests / minute; honor
Retry-Afteron 429 - Print the exact command and wait for user confirmation before any destructive admin operation
Check references before admin work:
- admin.md
- permissions.md for workspace / item ACL exposure audits
Definitions and deployment (item definitions, deployment pipelines, git integration, cicd)
| Command | Purpose | Example |
|---|---|---|
fab get -q "definition" | Read raw definition | fab get "ws/Model.SemanticModel" -q "definition" -f |
fab export | Export item to local | fab export "ws/Nb.Notebook" -o ./backup -f |
fab import | Import item from local | fab import "ws/Nb.Notebook" -i ./backup/Nb.Notebook -f |
fab cp | Copy between workspaces | fab cp "dev/Item" "prod.Workspace" -f |
fab api "deploymentPipelines" | Deployment pipelines API | fab api "deploymentPipelines" -q "value[]" |
Flags:
-o(output path forfab export)-i(input path or JSON body forfab import)--format(definition format for export / import)-f(skip overwrite and sensitivity prompts)
Every Fabric item has a serializable definition. Move definitions between environments depending on scope:
- Single item:
- Round-trip locally:
fab exportthenfab import(alwaysmkdir -pthe output directory first;fab exportdoes not create intermediate directories and fails with[InvalidPath]) - Same-tenant shortcut, no local hop:
fab cp "dev/Item" "prod.Workspace" - Semantic model as PBIP (TMDL + blank report):
- Power BI Desktop and git-ready format: `scripts/export_semantic_model_as_pbip.py`
- Full workspace snapshot (items + lakehouse files):
- Backups, offline analysis, cross-tenant forks: `scripts/download_workspace.py`
- Promotion between Dev, Test, Prod:
- Fabric deployment pipelines API (covers all item types)
- Power BI pipelines API (Power BI items only, but finer-grained deploy flags like
allowPurgeData,allowTakeOver) - When to use each, selective deploy, LRO polling: deployment-pipelines.md
- Git integration (connect workspace to repo, branch, commit, update from git):
- Workspace git section in workspaces.md
Check references before deploying:
- import-download-deploy.md ; export / import / copy / move, PBIP round-trips, migration patterns, rebinding gotchas
- deployment-pipelines.md
- semantic-models.md
- reports.md
- paginated-reports.md
- notebooks.md
- workspaces.md
Related skills
audit-tenant-settings(in thefabric-adminplugin) ; Fabric governance workflow covering tenant settings, delegated overrides (capacity / domain / workspace), and the Entra security groups those settings reference. Read-only; holds the curated metadata baseline and the audit + change-detection script.
Gotchas
- IMPORTANT: DON'T try to use
fab lson items that aren't data items (.Lakehouse, .Warehouse, etc); usefab lsto find workspaces and items, and usefab getto look at definitions - ALWAYS Use the
-fflag when usingfab get,fab import,fab export, etc. as described above - ONLY fallback to
fab apiwhen a command doesn't exist
References
Skill references:
- Import, Download, and Deploy - Export / import / copy / move items, PBIP round-trips, dev-to-prod migration patterns
- Querying Data - Query semantic models in DAX and lakehouses or warehouses in SQL with DuckDB
- Lakehouses - Endpoints, file/table operations, OneLake paths
- Warehouses - Create, browse, query via DuckDB, load data
- SQL Databases - Create, browse, query via DuckDB, auto-mirroring
- Semantic Models - TMDL, DAX, refresh, storage mode
- Reports - Export, import, visuals, fields
- Paginated Reports - RDL upload, export-to-file, datasources, parameters
- Notebooks - Python/PySpark kernels, metadata, cell CRUD, Livy execution, scheduling
- Workspaces - Create, manage, permissions
- Permissions - Sharing and distribution, workspace roles, item permissions, apps, embed, B2B, deployment pipeline permissions, licensing and capacity SKUs
- Deployment Pipelines - CI/CD, deploy stages, selective deploy, LRO polling
- Dataflows - Gen1 and Gen2, refresh, publish, admin
- Dashboards - Tiles, clone (dashboards are not reports)
- Org Apps - Read-only API for distributed content packages
- Scorecards - Goals, check-ins, status rules (Preview API)
- Gateways - Datasources, credentials, dataset binding
- Folders - Organize items into folders via API; includes best practices for structuring workspaces
- Tags - Create, apply, and audit tenant/domain tags on items and workspaces via
fab api(no nativefab tagcommand) - fab vs az CLI - When to use which; capacity, networking, Key Vault, monitoring, CMK, CI/CD
- Admin APIs - Cross-workspace search, tenant operations, governance
- API Reference - Capacities, domains, misc API patterns
- Connections - Create, update, list connections programmatically; credential types (WorkspaceIdentity, SPN, Basic); OAuth2 limitations
- Full Command Reference - All commands detailed
Scripts (scripts that you can execute):
- search_across_workspaces.py ; cross-workspace governance complement to
fab find(last visit, last refresh, owner, storage mode, capacity SKU, Copilot readiness); see workspaces.md for when to choose which - get-downstream-reports.py ; find all reports connected to a given semantic model across accessible workspaces (no admin required)
- execute_dax.py ; execute DAX queries against semantic models; output as table, csv, or json
- query_lakehouse_duckdb.py ; query lakehouse or warehouse Delta tables via DuckDB against OneLake (reuses
az login); output as table, csv, or json - query_sql_endpoint.py ; query lakehouse SQL endpoint, warehouse, or SQL database via
sqlcmd(reusesaz loginthroughActiveDirectoryAzCli); output as table, csv, or json - create_direct_lake_model.py ; create a Direct Lake semantic model from lakehouse tables
- export_semantic_model_as_pbip.py ; export a semantic model as a PBIP project (TMDL definition + blank report)
- download_workspace.py ; download a full workspace with all item definitions and lakehouse files
See scripts/README.md for detailed usage, arguments, and examples. Always search the scripts/ folder before writing a new helper; a script may already exist for the task.
External references (request markdown when possible):
- fab CLI: GitHub Source | Docs
- Microsoft: Fabric CLI Learn
- APIs: Fabric API | Power BI API
- DAX: dax.guide - use
dax.guide/<function>/e.g.dax.guide/addcolumns/ - Power Query: powerquery.guide - use
powerquery.guide/function/<function> - Power Query Best Practices
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernel_info": {
"name": "synapse_pyspark"
},
"language_info": {
"name": "python"
},
"microsoft": {
"language": "python",
"language_group": "synapse_pyspark"
},
"dependencies": {
"lakehouse": {
"default_lakehouse": "<lakehouse-guid>",
"default_lakehouse_name": "<LakehouseName>",
"default_lakehouse_workspace_id": "<workspace-guid>",
"known_lakehouses": [
{ "id": "<lakehouse-guid>" }
]
}
}
},
"cells": [
{
"cell_type": "markdown",
"source": [
"# Data Freshness Check\n",
"\n",
"Query lakehouse tables to verify latest data dates and row counts.\n",
"\n",
"- Input: Lakehouse delta tables\n",
"- Output: Print freshness summary"
],
"metadata": {
"nteract": { "transient": { "deleting": false } },
"microsoft": { "language": "python", "language_group": "synapse_pyspark" }
}
},
{
"cell_type": "code",
"source": [
"from pyspark.sql import functions as F\n",
"\n",
"# Read from lakehouse table using three-part naming\n",
"df = spark.sql(\"\"\"\n",
" SELECT \n",
" max(date_key) as latest_date,\n",
" min(date_key) as earliest_date,\n",
" count(*) as total_rows\n",
" FROM LakehouseName.schema.table_name\n",
"\"\"\")\n",
"\n",
"# Print results (display() does not show output in fab job run)\n",
"pandas_df = df.toPandas()\n",
"print(pandas_df)\n",
"print(f\"\\nLatest date: {pandas_df.iloc[0]['latest_date']}\")"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "synapse_pyspark" }
}
},
{
"cell_type": "markdown",
"source": [
"## Write results to lakehouse"
],
"metadata": {
"microsoft": { "language": "python", "language_group": "synapse_pyspark" }
}
},
{
"cell_type": "code",
"source": [
"# Write DataFrame to lakehouse table\n",
"# Three-part naming: LakehouseName.schema.table\n",
"df.write.mode(\"overwrite\").option(\"overwriteSchema\", \"true\").saveAsTable(\"LakehouseName.schema.freshness_log\")\n",
"print(\"Written to lakehouse\")"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "synapse_pyspark" }
}
}
]
}
{
"nbformat": 4,
"nbformat_minor": 5,
"metadata": {
"kernel_info": {
"name": "jupyter",
"jupyter_kernel_name": "python3.11"
},
"language_info": {
"name": "python"
},
"microsoft": {
"language": "python",
"language_group": "jupyter_python"
},
"kernelspec": {
"name": "jupyter",
"display_name": "Jupyter"
},
"dependencies": {
"lakehouse": {
"default_lakehouse": "<lakehouse-guid>",
"default_lakehouse_name": "<LakehouseName>",
"default_lakehouse_workspace_id": "<workspace-guid>",
"known_lakehouses": [
{ "id": "<lakehouse-guid>" }
]
},
"warehouse": {
"default_warehouse": "<warehouse-guid>",
"known_warehouses": [
{ "id": "<warehouse-guid>", "type": "Datawarehouse" }
]
}
}
},
"cells": [
{
"cell_type": "markdown",
"source": [
"# Python Notebook Example\n",
"\n",
"Demonstrates reading from lakehouse with DuckDB/delta-rs and writing to warehouse via T-SQL.\n",
"Python notebooks start in ~5 seconds (no Spark cluster) and come with DuckDB, Polars, and delta-rs pre-installed."
],
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "code",
"source": [
"import sys\n",
"print(f'Python {sys.version}')"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "markdown",
"source": [
"## Read lakehouse Delta table with delta-rs"
],
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "code",
"source": [
"from deltalake import DeltaTable\n",
"\n",
"# Option 1: Local path (requires attached lakehouse)\n",
"dt = DeltaTable('/lakehouse/default/Tables/my_table')\n",
"df = dt.to_pandas()\n",
"print(df.head())\n",
"\n",
"# Option 2: ABFS path (any lakehouse; no attachment needed)\n",
"access_token = notebookutils.credentials.getToken('storage')\n",
"storage_options = {'bearer_token': access_token, 'use_fabric_endpoint': 'true'}\n",
"\n",
"dt = DeltaTable(\n",
" 'abfss://<workspace-guid>@onelake.dfs.fabric.microsoft.com/<lakehouse-guid>/Tables/schema/table',\n",
" storage_options=storage_options\n",
")\n",
"df = dt.to_pandas()\n",
"print(df.head())"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "markdown",
"source": [
"## Read lakehouse Delta table with DuckDB"
],
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "code",
"source": [
"import duckdb\n",
"from deltalake import DeltaTable\n",
"\n",
"# DuckDB can query delta-rs Arrow datasets with filter pushdown\n",
"access_token = notebookutils.credentials.getToken('storage')\n",
"storage_options = {'bearer_token': access_token, 'use_fabric_endpoint': 'true'}\n",
"\n",
"dt = DeltaTable(\n",
" 'abfss://<workspace-guid>@onelake.dfs.fabric.microsoft.com/<lakehouse-guid>/Tables/schema/table',\n",
" storage_options=storage_options\n",
")\n",
"arrow_ds = dt.to_pyarrow_dataset()\n",
"\n",
"result = duckdb.sql('SELECT count(*) as rows, max(date_key) as latest FROM arrow_ds').df()\n",
"print(result)"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "markdown",
"source": [
"## Write to lakehouse with delta-rs"
],
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "code",
"source": [
"from deltalake.writer import write_deltalake\n",
"import pandas as pd\n",
"\n",
"df = pd.DataFrame({'id': [1, 2, 3], 'name': ['a', 'b', 'c']})\n",
"write_deltalake('/lakehouse/default/Tables/my_output_table', df, mode='overwrite')\n",
"print('Written to lakehouse')"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "markdown",
"source": [
"## Query warehouse with T-SQL via notebookutils.data"
],
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
},
{
"cell_type": "code",
"source": [
"# connect_to_artifact supports: Warehouse (full DML), Lakehouse (read-only),\n",
"# SQLDatabase (full DML), MirroredDatabase (read-only)\n",
"with notebookutils.data.connect_to_artifact('WarehouseName') as conn:\n",
" conn.query('CREATE TABLE dbo.test (id INT, name VARCHAR(100))')\n",
" conn.query(\"INSERT INTO dbo.test VALUES (1, 'hello'), (2, 'world')\")\n",
" df = conn.query('SELECT * FROM dbo.test')\n",
" print(df)"
],
"outputs": [],
"execution_count": null,
"metadata": {
"microsoft": { "language": "python", "language_group": "jupyter_python" }
}
}
]
}
Admin API Operations
Guide for Fabric/Power BI admin-level API operations using fab. These APIs require admin privileges and enable cross-workspace discovery, tenant-wide operations, and governance.
Docs
For more info use:
mslearn searchormslearn fetch- Microsoft Learn MCP server (
microsoft_docs_search,microsoft_docs_fetch)
Prerequisites
- Fabric Admin or Power BI Admin role
- Or delegated admin permissions via service principal
Check your access with a cheap read probe; fab api returns a {status_code, text} envelope, so query the status directly:
fab api "admin/tenantsettings" -q "status_code"
# 200 = admin read access; 401 = not authed; 403 = not adminAdmin access isn't binary. Read-only admin, full admin, and service principals with allowlist scoping behave differently per endpoint, so a successful probe doesn't guarantee the next call will work. For write endpoints there's no safe dry-run; you discover 401/403 on the real call. When in doubt, test the specific endpoint you intend to use.
Rate limit: admin write endpoints are capped at 25 requests/minute; watch for 429 and respect Retry-After.
Tenant Settings, Security Groups, Workspace Permissions
Use the `audit-tenant-settings` skill (in the `fabric-admin` plugin) for any audit, drift, change-detection, or governance question touching tenant settings, delegated overrides, or the Entra ID security groups that scope them. That skill owns the curated baseline, the audit script, and the SG investigation workflow. The patterns below cover the raw API mechanics the skill relies on.
List tenant settings
fab api "admin/tenantsettings"
# Filter by keyword in the UI title (note the text. prefix; fab api -q
# runs JMESPath against the full {status_code, text} response envelope)
fab api "admin/tenantsettings" -q "text.tenantSettings[?contains(title, 'recovery')]"
# Just name + enabled state
fab api "admin/tenantsettings" -q "text.tenantSettings[].{name: settingName, enabled: enabled}"Each setting has settingName, title, enabled, canSpecifySecurityGroups, tenantSettingGroup, and optional properties, enabledSecurityGroups, excludedSecurityGroups, delegateToCapacity, delegateToDomain, delegateToWorkspace.
Update a tenant setting
POST /v1/admin/tenantsettings/{settingName}/updatePass the body via -i <file>. enabled is required; other fields are preserved or cleared based on what you send.
cat > /tmp/setting.json <<'EOF'
{
"enabled": true,
"properties": [
{"name": "ArtifactRetentionPeriod", "value": "7", "type": "Integer"}
]
}
EOF
fab api -X post "admin/tenantsettings/ConfigureArtifactRetentionPeriod/update" -i /tmp/setting.jsonProperty type values: Boolean, Integer, FreeText, Url, MailEnabledSecurityGroup. The value is always a string.
Capacity, domain, and workspace overrides
Delegated overrides replace a tenant-wide setting for a specific scope, so any audit must enumerate them before concluding what posture a workspace actually sees. Overrides only exist when the parent setting has delegateToCapacity / delegateToDomain / delegateToWorkspace set to true. The three list endpoints below return every override defined at that scope across the tenant in a single call, so you can diff live state vs. parent tenant setting:
Reads (all scopes):
# Tenant-wide: every override across all capacities / domains / workspaces
fab api "admin/capacities/delegatedTenantSettingOverrides"
fab api "admin/domains/delegatedTenantSettingOverrides"
fab api "admin/workspaces/delegatedTenantSettingOverrides"
# Scoped: overrides for a specific capacity (only capacity has a scoped variant;
# domain- and workspace-scoped endpoints return 404 and must be filtered client-side
# from the tenant-wide list above).
fab api "admin/capacities/{capacityId}/delegatedTenantSettingOverrides"The capacity tenant-wide response ships both overrides and value arrays (legacy + current schema); the domain and workspace responses ship only value. All three return continuationUri / continuationToken when paged.
Writes (capacity only): the Fabric admin REST API only exposes Update and Delete for capacity overrides. Domain and workspace overrides are read-only through the admin API and must be managed in the admin portal UI (or via the domain/workspace admin UIs) by someone with the appropriate admin role.
# Update a capacity override (same body shape as tenant-wide update)
cat > /tmp/override.json <<'EOF'
{
"enabled": true,
"properties": [
{"name": "ArtifactRetentionPeriod", "value": "14", "type": "Integer"}
]
}
EOF
fab api -X post \
"admin/capacities/{capacityId}/delegatedTenantSettingOverrides/{settingName}/update" \
-i /tmp/override.json
# Remove a capacity override
fab api -X delete \
"admin/capacities/{capacityId}/delegatedTenantSettingOverrides/{settingName}"Ref: Admin - Tenants REST API. The doc enumerates Delete/List/Update operations and none exist for domain or workspace overrides.
A setting that is enabled: true tenant-wide can still be disabled (or security-group-restricted) on a specific capacity/domain/workspace via an override, and vice versa; always check overrides before concluding a governance finding.
Worked example: workspace retention
Setting name: ConfigureFolderRetentionPeriod. Controls how long deleted collaborative workspaces can be restored. Default enabled at 7 days; valid range 7 to 90. Personal workspaces (My workspace) are fixed at 30 days and cannot be changed.
echo '{"enabled": true, "properties": [{"name": "FolderRetentionPeriod", "value": "7", "type": "Integer"}]}' \
> /tmp/ws-retention.json
fab api -X post "admin/tenantsettings/ConfigureFolderRetentionPeriod/update" -i /tmp/ws-retention.jsonWorked example: enable Fabric item recovery
The Fabric item recovery setting (ConfigureArtifactRetentionPeriod) controls whether fab rm soft-deletes items into a workspace recycle bin. Default is off; valid retention range is 7 to 90 days.
# Current state
fab api "admin/tenantsettings" \
-q "text.tenantSettings[?settingName=='ConfigureArtifactRetentionPeriod']"
# Enable with 7-day retention
cat > /tmp/recovery.json <<'EOF'
{
"enabled": true,
"properties": [
{"name": "ArtifactRetentionPeriod", "value": "7", "type": "Integer"}
]
}
EOF
fab api -X post "admin/tenantsettings/ConfigureArtifactRetentionPeriod/update" -i /tmp/recovery.json
# Disable
echo '{"enabled": false}' > /tmp/recovery-off.json
fab api -X post "admin/tenantsettings/ConfigureArtifactRetentionPeriod/update" -i /tmp/recovery-off.jsonOnce enabled, deletes land in workspaces/{workspaceId}/recoverableItems; see reference.md > Recovering deleted items for the restore flow.
Gotchas
fab api -q <jmespath>runs against the full{status_code, text}envelope, so filters must start withtext.(e.g.text.tenantSettings[?...],text.value[?...]). Queries that forget the prefix silently returnNone.- Empty-body POSTs to endpoints that return
202 null(e.g.recoverableItems/.../recover) can occasionally surface[UnexpectedError] Expecting value: line 1 column 1 (char 0)fromfab api's JSON parser. The operation still succeeds; verify with a follow-upfab existsor list call and retry if necessary. - API setting names differ from admin portal UI titles; always list first.
enabled: falseon update does not preserve priorproperties; resend them if needed.- Updating a parent setting does not retroactively rewrite existing capacity/domain/workspace overrides; adjust overrides explicitly.
- Security-group-scoped settings require
canSpecifySecurityGroups: trueand a validgraphIdinenabledSecurityGroups/excludedSecurityGroups. - Service principals hit 401 on write endpoints unless the Service principals can access admin APIs used for updates tenant setting is on, even with
Tenant.ReadWrite.All.
Cross-Workspace Item Discovery
For routine item discovery (find by name, filter by type), use fab find first; it works for any authenticated user without admin role. See workspaces.md for usage and the delta against the DataHub V2 governance script.
The admin API path below is for tenant-wide audits where admin role is already in scope (e.g. responding to a security review, populating a governance dashboard). It returns every item in every workspace regardless of the caller's permissions.
Find Items by Type (admin)
# All semantic models across tenant
fab api "admin/items" -P "type=SemanticModel"
# All notebooks
fab api "admin/items" -P "type=Notebook"
# All lakehouses
fab api "admin/items" -P "type=Lakehouse"
# By name pattern
fab api "admin/items" -P "type=SemanticModel" -q "itemEntities[?contains(name, 'Sales')]"Available Item Types
SemanticModel Report Dashboard Notebook
Lakehouse Warehouse DataPipeline Dataflow
Environment SparkJobDef CopyJob Reflex
Ontology GraphModel Exploration OrgAppExtract Item Details
# Get item IDs and workspace IDs
fab api "admin/items" -P "type=Lakehouse" -q "itemEntities[].{name:name,id:id,workspace:workspaceId}"
# Find item's workspace name
ITEM=$(fab api "admin/items" -P "type=SemanticModel" -q "itemEntities[?name=='Sales Model'] | [0]")
WS_ID=$(echo "$ITEM" | jq -r '.workspaceId')
fab api "admin/workspaces/$WS_ID" -q "displayName"Workspace Administration
List All Workspaces
# All workspaces in tenant
fab api "admin/workspaces"
# Filter by state
fab api "admin/workspaces" -q "workspaces[?state=='Active']"
# Get workspace users (preferred: native command)
fab acl ls "ws.Workspace"
fab acl get "ws.Workspace"Workspace Governance
# Get workspace capacity assignment
fab api "admin/workspaces/<workspace-id>" -q "capacityId"
# List workspaces on a capacity
fab api "admin/capacities/<capacity-id>/workspaces"Capacity Administration
# List all capacities
fab api "admin/capacities"
# Get capacity details
fab api "admin/capacities/<capacity-id>"
# Get capacity workloads
fab api "admin/capacities/<capacity-id>/workloads"
# Native alternatives for capacity management:
fab start .capacities/<capacity-name>
fab stop .capacities/<capacity-name>
fab assign .capacities/<capacity-name> -W ws.WorkspaceDataset/Model Administration
# Get all datasets in tenant (Power BI API)
fab api -A powerbi "admin/datasets"
# Get dataset users (preferred: native command at workspace scope)
fab acl ls "ws.Workspace/Model.SemanticModel"
# Get datasources for a dataset
fab api -A powerbi "admin/datasets/<dataset-id>/datasources"Report Administration
# Get all reports in tenant
fab api -A powerbi "admin/reports"
# Get report users (preferred: native command)
fab acl ls "ws.Workspace/Report.Report"
# Get reports in a workspace
fab api -A powerbi "admin/groups/<workspace-id>/reports"Governance: Metadata Scanner API
Use case: catalogue every workspace, item, table, column, measure, DAX expression, and data source in the tenant for data-governance, Purview ingestion, or lineage tooling. This is the only admin API that returns subartifact metadata (model schemas, M queries, DAX).
Scanner API flow
Four endpoints under the powerbi audience:
GET admin/workspaces/modified (which workspaces changed)
POST admin/workspaces/getInfo (start a scan for up to 100 workspaces)
GET admin/workspaces/scanStatus/{scanId} (poll until Succeeded)
GET admin/workspaces/scanResult/{scanId} (fetch metadata payload)Rules:
- Max 100 workspace IDs per
getInfocall. - No more than 16 concurrent scans per tenant.
- Poll
scanStatusat 30-60 second intervals. modifiedSinceaccepts ISO 8601 and must be within the last 30 days.- Tenant admin must have Enhance admin APIs responses with detailed metadata and Enhance admin APIs responses with DAX and mashup expressions enabled (see Tenant Settings above; setting names
AdminApisIncludeDetailedMetadataandAdminApisIncludeExpressions).
Full scan
# 1. List every workspace in the tenant (exclude personal workspaces)
fab api -A powerbi "admin/workspaces/modified" -P "excludePersonalWorkspaces=true" \
-q "text[].id" > /tmp/ws-all.json
# 2. Kick off a scan for a batch of up to 100 workspace IDs
cat > /tmp/scan-body.json <<'EOF'
{
"workspaces": [
"aaaaaaaa-0000-1111-2222-333333333333",
"bbbbbbbb-0000-1111-2222-333333333333"
]
}
EOF
SCAN_ID=$(fab api -A powerbi -X post \
"admin/workspaces/getInfo" \
-P "lineage=true,datasourceDetails=true,datasetSchema=true,datasetExpressions=true,getArtifactUsers=true" \
-i /tmp/scan-body.json \
-q "text.id" | tr -d '"')
# 3. Poll scan status
while :; do
STATUS=$(fab api -A powerbi "admin/workspaces/scanStatus/$SCAN_ID" -q "text.status" | tr -d '"')
echo "status: $STATUS"
[ "$STATUS" = "Succeeded" ] && break
[ "$STATUS" = "Failed" ] && { echo "scan failed"; exit 1; }
sleep 30
done
# 4. Fetch results
fab api -A powerbi "admin/workspaces/scanResult/$SCAN_ID" > /tmp/scan-result.jsonIncremental scan
# Workspaces changed since the last scan time
LAST_SCAN="2026-04-13T00:00:00.0000000Z"
fab api -A powerbi "admin/workspaces/modified" \
-P "modifiedSince=$LAST_SCAN,excludePersonalWorkspaces=true"
# ...then batch into getInfo / scanStatus / scanResult as aboveScanner gotchas
- Semantic models that haven't been refreshed or republished return lineage only, no subartifact schema.
- DirectQuery-only semantic models need at least one report interaction before subartifact metadata is populated.
- Shared-workspace semantic models over 1 GB return no subartifact metadata (Premium/Fabric capacities have no limit).
- Unsupported types surface a
schemaRetrievalErrorfield instead of schema: real-time datasets, OLS-enabled models, live-connect AS Azure / AS on-prem, Excel full fidelity. - Scanner APIs require
-A powerbi; hitting them under the defaultfabricaudience returns 404. - Running under a service principal requires Service principals can access read-only admin APIs enabled and the SP added to an allowed security group.
Audit: Activity Events
Use case: investigate who did what during an incident, export audit trails for compliance, feed a SIEM.
# Activity events for a single day (max window per call = 1 day; 30-day history)
fab api -A powerbi "admin/activityevents" \
-P "startDateTime='2026-04-13T00:00:00Z',endDateTime='2026-04-13T23:59:59Z'"
# Activity for a specific operation
fab api -A powerbi "admin/activityevents" \
-P "startDateTime='2026-04-13T00:00:00Z',endDateTime='2026-04-13T23:59:59Z',$filter=Activity eq 'DeleteDataset'"Continuation token pattern:
URI="admin/activityevents?startDateTime='2026-04-13T00:00:00Z'&endDateTime='2026-04-13T23:59:59Z'"
while :; do
RESP=$(fab api -A powerbi "$URI")
echo "$RESP" | jq '.text.activityEventEntities[]'
TOKEN=$(echo "$RESP" | jq -r '.text.continuationToken // empty')
[ -z "$TOKEN" ] && break
URI="admin/activityevents?continuationToken='$TOKEN'"
doneCommon Activity values: ViewReport, ViewDashboard, CreateDataset, DeleteDataset, ExportReport, UpdateWorkspaceAccess, ShareReport, Admin settings changes (UpdatedAdminFeatureSwitch).
Audit: User Access and Orphans
Use case: offboarding, license reviews, finding workspaces with no owners, finding items a departing user owns.
# What items does this user have access to across the tenant
fab api -A powerbi "admin/users/{userGraphId}/artifactAccess"
# Workspace users (preferred: native command)
fab acl ls "ws.Workspace"
# Find workspaces with no active admins (orphan hunt)
fab api -A powerbi "admin/groups?%24expand=users&%24filter=state eq 'Active'" \
-q "text.value[?!users[?groupUserAccessRight=='Admin']].{id:id,name:name}"Monitoring: Refreshes
Use case: detect failing refreshes across every workspace, build a refresh SLA dashboard, catch stuck semantic models.
# Top refreshables across tenant (sorted by last refresh)
fab api -A powerbi "admin/capacities/refreshables?%24top=50&%24expand=capacity,group"
# Refresh history for a specific dataset
fab api -A powerbi "admin/groups/{workspaceId}/datasets/{datasetId}/refreshes?%24top=10"
# Refresh schedule
fab api -A powerbi "admin/groups/{workspaceId}/datasets/{datasetId}/refreshSchedule"
# Datasets pinned to a capacity
fab api -A powerbi "admin/capacities/{capacityId}/refreshables"Fields worth tracking per refreshable: lastRefresh.status, lastRefresh.endTime, averageDuration, medianDuration, refreshCount, refreshFailures, refreshesPerDay.
Capacity Health
Use case: which workspaces live on which capacity, workload enablement, pause/resume, throttling events.
# List all capacities with SKU and state
fab api -A powerbi "admin/capacities"
# Workloads enabled on a capacity
fab api -A powerbi "admin/capacities/{capacityId}/Workloads"
# Pause / resume a Fabric capacity
fab stop .capacities/{capacity-name}
fab start .capacities/{capacity-name}
# Reassign a stranded workspace to a healthy capacity (preferred: native)
fab assign .capacities/{capacity-name} -W ws.Workspace -fGateways and Data Sources
Use case: find which items depend on an on-premises gateway, audit gateway admins, locate a stale gateway cluster.
# All gateway clusters in tenant
fab api -A powerbi "admin/gatewayClusters"
# Data sources attached to a gateway
fab api -A powerbi "admin/gatewayClusters/{clusterId}/datasources"
# Which datasets use a given data source
fab api -A powerbi "admin/datasources/{datasourceId}/datasets"Deployment Pipelines
Use case: audit pipeline stage assignments, diff dev/test/prod content, bulk-inspect pipeline users.
fab api -A powerbi "admin/pipelines"
fab api -A powerbi "admin/pipelines/{pipelineId}/users"
fab api -A powerbi "admin/pipelines/{pipelineId}/stages"
fab api -A powerbi "admin/pipelines/{pipelineId}/operations"Dataflows
Use case: tenant-wide dataflow inventory, usage of a specific dataflow across workspaces.
# All dataflows in tenant
fab api -A powerbi "admin/dataflows"
# Downstream datasets for a dataflow
fab api -A powerbi "admin/dataflows/{dataflowId}/datasources"
# Users with access
fab api -A powerbi "admin/dataflows/{dataflowId}/users"Workspace Lifecycle
Use case: recover deleted workspaces during their retention window, reassign a workspace owner after offboarding.
# List deleted workspaces still within retention
fab api "admin/workspaces?%24filter=state eq 'Deleted'"
# Restore a deleted workspace (assign a new owner)
cat > /tmp/restore.json <<'EOF'
{
"emailAddress": "new.owner@contoso.com",
"name": "Recovered Workspace"
}
EOF
fab api -A powerbi -X post "admin/groups/{workspaceId}/restore" -i /tmp/restore.json
# Update workspace properties (rename, change description)
fab api -A powerbi -X patch "admin/groups/{workspaceId}" -i /tmp/update.jsonWorkspace retention (separate from Item Recovery; default 90 days after deletion) is controlled by ConfigureFolderRetentionPeriod in Tenant Settings.
Common Patterns
Find Item Across Workspaces
# Search for a model by name
fab api "admin/items" -P "type=SemanticModel" \
-q "itemEntities[?contains(name, 'keyword')] | [0].{name:name,id:id,workspace:workspaceId}"Get Full Item Path
# Get workspace name + item name for fab path
ITEM=$(fab api "admin/items" -P "type=Notebook" -q "itemEntities[?name=='ETL Pipeline'] | [0]")
WS_ID=$(echo "$ITEM" | jq -r '.workspaceId')
ITEM_NAME=$(echo "$ITEM" | jq -r '.name')
WS_NAME=$(fab api "admin/workspaces/$WS_ID" -q "displayName" | tr -d '"')
echo "$WS_NAME.Workspace/$ITEM_NAME.Notebook"Audit Item Modifications
# Get items modified recently
fab api "admin/items" -P "type=Report" \
-q "itemEntities | sort_by(@, &lastUpdatedDate) | reverse(@) | [:10]"Security & Governance
Get Item Permissions
Use native ACL commands; they cover workspace, semantic model, and report permissions without admin API calls.
# Workspace permissions
fab acl ls "ws.Workspace"
fab acl get "ws.Workspace"
# Semantic model permissions
fab acl ls "ws.Workspace/Model.SemanticModel"
# Report permissions
fab acl ls "ws.Workspace/Report.Report"
# Grant or revoke access
fab acl set "ws.Workspace" -I <objectId> -R Member
fab acl rm "ws.Workspace" -I <upn-or-clientId> -fEncryption Keys
# Get tenant encryption keys
fab api -A powerbi "admin/tenantKeys"Pagination
Admin APIs return paginated results. Check for continuation:
# First page
RESULT=$(fab api "admin/items" -P "type=SemanticModel")
# Check for more
echo "$RESULT" | jq '.continuationUri'
# If not null, fetch next page
fab api "<continuation-uri>"Error Handling
Common admin API errors:
| Error | Cause | Solution |
|---|---|---|
| 401 | Not authenticated | Run fab auth login |
| 403 | Not admin | Request admin role |
| 404 | Item not found | Check item exists |
| 429 | Rate limited | Wait and retry |
Best Practices
1. Cache results - Admin APIs can be slow; cache for repeated queries 2. Use filters - Always filter by type when possible 3. Paginate - Handle continuation for large tenants 4. Rate limit - Space out bulk operations 5. Audit - Log admin operations for compliance
Connections API
Programmatically create, update, list, and delete Fabric cloud connections.
List Connections
fab ls .connections -lOr via API:
GET https://api.fabric.microsoft.com/v1/connectionsCreate Connection
POST https://api.fabric.microsoft.com/v1/connectionsWith WorkspaceIdentity (no secrets needed)
{
"connectivityType": "ShareableCloud",
"displayName": "MyLakehouseConnection",
"connectionDetails": {
"type": "SQL",
"creationMethod": "SQL",
"parameters": [
{"dataType": "Text", "name": "server", "value": "<endpoint>.datawarehouse.fabric.microsoft.com"},
{"dataType": "Text", "name": "database", "value": "<LakehouseName>"}
]
},
"privacyLevel": "Organizational",
"credentialDetails": {
"singleSignOnType": "None",
"connectionEncryption": "Encrypted",
"skipTestConnection": false,
"credentials": {
"credentialType": "WorkspaceIdentity"
}
}
}WorkspaceIdentity uses the workspace's managed service principal. No passwords, secrets, or OAuth consent. Supported for Fabric data sources (SQL, ADLS connectors).
With Basic Auth
{
"connectivityType": "ShareableCloud",
"displayName": "MyConnection",
"connectionDetails": {
"type": "SQL",
"creationMethod": "SQL",
"parameters": [
{"dataType": "Text", "name": "server", "value": "myserver.database.windows.net"},
{"dataType": "Text", "name": "database", "value": "mydb"}
]
},
"privacyLevel": "Organizational",
"credentialDetails": {
"singleSignOnType": "None",
"connectionEncryption": "NotEncrypted",
"skipTestConnection": false,
"credentials": {
"credentialType": "Basic",
"username": "admin",
"password": "********"
}
}
}With Service Principal
{
"credentialDetails": {
"credentials": {
"credentialType": "ServicePrincipal",
"servicePrincipalClientId": "<client-id>",
"servicePrincipalSecret": "<secret>",
"tenantId": "<tenant-id>"
}
}
}Supported Credential Types
| Type | API Support | Notes |
|---|---|---|
| WorkspaceIdentity | Yes | No secrets; uses workspace managed identity |
| Basic | Yes | Username + password |
| ServicePrincipal | Yes | Client ID + secret + tenant |
| Key | Yes | API key or account key |
| SharedAccessSignature | Yes | SAS token |
| Anonymous | Yes | No credentials |
| OAuth2 | No | Requires browser consent; cannot be created via API |
| Windows | No | On-premises gateway only |
Update Connection
PATCH https://api.fabric.microsoft.com/v1/connections/{connectionId}Update display name or credential details. Cannot change credential type (e.g. OAuth2 to WorkspaceIdentity).
Via fab:
fab set ".connections/<Name>.Connection" -q displayName -i "New Name"
fab set ".connections/<Name>.Connection" -q credentialDetails -i @creds.jsonDelete Connection
DELETE https://api.fabric.microsoft.com/v1/connections/{connectionId}Via fab:
fab rm ".connections/<Name>.Connection" -fGet Connection Details
fab get .connections/<ConnectionName>.Connection
fab get .connections/<ConnectionName>.Connection -q "connectionDetails"Key Limitation: OAuth2 Connections
OAuth2 connections cannot be created or refreshed via API. They require an interactive browser OAuth consent flow. This affects:
- Dataflow
executeQueryAPI: needs OAuth-authenticated connections for data source access - Semantic model refresh: needs OAuth connections for cloud data sources
Workarounds:
- Use
WorkspaceIdentityorServicePrincipalcredential types instead of OAuth2 - For OAuth2: create the connection once in the portal, then reference it by ID in automation
- Use
fabCLI connections management:fab ls .connections,fab get .connections/Name.Connection
Microsoft Documentation
Dashboard Operations
Dashboards are a distinct Power BI item type -- they are not reports. A dashboard is a single-page canvas of pinned tiles, where each tile displays a snapshot from a report visual, Q&A query, or standalone widget (image, text, video, streaming data). Unlike reports, dashboards cannot be authored in Power BI Desktop; they exist only in the Power BI service. Dashboards do not have pages, filters, slicers, or interactive visuals -- they are curated, at-a-glance views that link back to the underlying reports.
All examples target the workspace-scoped ("In Group") endpoints unless noted. Replace $WS_ID with the target workspace ID and $DASH_ID, $TILE_ID with the relevant object IDs throughout.
# Resolve workspace ID once
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')---
List Dashboards
fab api -A powerbi "groups/$WS_ID/dashboards"Filter to names and IDs:
fab api -A powerbi "groups/$WS_ID/dashboards" -q "value[].{id:id, name:displayName}"Get a Single Dashboard
fab api -A powerbi "groups/$WS_ID/dashboards/$DASH_ID"Create a Dashboard
Create an empty dashboard. There is no way to add tiles via REST API -- tiles are pinned through the Power BI service UI only.
fab api -A powerbi -X post "groups/$WS_ID/dashboards" \
-i '{"name": "Sales Overview"}'The response returns the new dashboard object with its id, embedUrl, and webUrl.
Delete a Dashboard
fab api -A powerbi -X delete "groups/$WS_ID/dashboards/$DASH_ID"Returns HTTP 200 on success with an empty body.
---
Tiles
List Tiles
fab api -A powerbi "groups/$WS_ID/dashboards/$DASH_ID/tiles"Each tile includes reportId and datasetId when the tile originates from a report visual or Q&A query. Standalone widget tiles (image, text, video) do not carry these fields.
Get a Single Tile
fab api -A powerbi "groups/$WS_ID/dashboards/$DASH_ID/tiles/$TILE_ID"Clone a Tile
Clone a tile to the same or a different dashboard. Optionally rebind the tile to a different report and/or semantic model in the target workspace.
fab api -A powerbi -X post \
"groups/$WS_ID/dashboards/$DASH_ID/tiles/$TILE_ID/Clone" \
-i '{
"targetDashboardId": "<target-dash-id>",
"targetWorkspaceId": "<target-ws-id>",
"targetReportId": "<target-report-id>",
"targetModelId": "<target-model-id>",
"positionConflictAction": "Tail"
}'Parameter notes:
targetDashboardId-- required. The destination dashboard.targetWorkspaceId-- optional. Omit or pass an empty GUID to target My Workspace.targetReportId/targetModelId-- optional. When cloning cross-workspace
without specifying these, the tile's report/semantic model links are removed and the tile will appear broken.
positionConflictAction--Tail(append to end) orAbort(fail if conflict).
Clone All Tiles Between Dashboards
No single endpoint clones an entire dashboard. Iterate tiles instead:
# 1. List source tiles
TILES=$(fab api -A powerbi "groups/$WS_ID/dashboards/$DASH_ID/tiles" \
-q "value[].id" | jq -r '.[]')
# 2. Clone each tile to the target dashboard
for TILE in $TILES; do
fab api -A powerbi -X post \
"groups/$WS_ID/dashboards/$DASH_ID/tiles/$TILE/Clone" \
-i "{\"targetDashboardId\": \"<target-dash-id>\", \"positionConflictAction\": \"Tail\"}"
done---
Admin Endpoints
Admin endpoints require Tenant.Read.All or Tenant.ReadWrite.All and are rate-limited to 50 requests/hour or 5 requests/minute per tenant.
# List all dashboards across the tenant
fab api -A powerbi "admin/dashboards"
# List dashboards in a specific workspace (admin scope)
fab api -A powerbi "admin/groups/$WS_ID/dashboards"
# Get tiles for a dashboard (admin scope)
fab api -A powerbi "admin/dashboards/$DASH_ID/tiles"
# Get dashboard users/permissions
fab api -A powerbi "admin/dashboards/$DASH_ID/users"
# Get dashboard subscriptions (preview)
fab api -A powerbi "admin/dashboards/$DASH_ID/subscriptions"Expand tiles inline on the admin list call with OData $expand:
fab api -A powerbi 'admin/groups/$WS_ID/dashboards?$expand=tiles'---
Object Models
Dashboard
Dashboard {
id string (uuid)
displayName string
embedUrl string
webUrl string
isReadOnly boolean
appId string -- present only when the dashboard belongs to an app
}The admin variant (AdminDashboard) adds workspaceId and optionally tiles[] when $expand=tiles is used.
Tile
Tile {
id string (uuid)
title string
embedUrl string
embedData string
rowSpan integer
colSpan integer
reportId string (uuid) -- only for report-sourced tiles
datasetId string -- only for report or Q&A tiles
}Permission Levels
| Value | Access |
|---|---|
| None | No access |
| Read | View only |
| ReadWrite | View and edit |
| ReadReshare | View and reshare |
| ReadCopy | View and copy |
| Owner | Full control (view, edit, reshare) |
---
Limitations
- No programmatic pin. Pinning a report visual to a dashboard is a UI-only
action. The REST API cannot create tiles from report visuals, Q&A queries, or standalone widgets (image, text, video, web content, streaming data).
- No dashboard clone. There is no endpoint to duplicate an entire dashboard.
Clone tiles individually to approximate this.
- No tile update. Tile position, size, and content cannot be modified via API.
- No tile delete. Individual tiles cannot be removed through the API.
- Tile title gap. Titles edited in the source report before pinning are not
returned by the tiles API.
- Deprecated fields. The
usersandsubscriptionsarrays on the Dashboard
response payload are being deprecated. Use the admin endpoints instead.
---
Related Admin Operations
Several tenant-wide admin operations touch dashboards indirectly:
- Metadata scanning (
PostWorkspaceInfo+GetScanResult) -- returns
dashboards with detailed metadata including tile info.
- Sensitivity labels (
SetLabelsAsAdmin/RemoveLabelsAsAdmin) -- apply or
remove information protection labels on dashboards.
- Published-to-web audit (
PublishedToWeb) -- find dashboards published to
the public web.
- Unused artifacts (
GetUnusedArtifactsAsAdmin) -- identify dashboards in a
workspace not accessed within 30 days.
Dataflow Operations
Dataflows come in two generations with completely different API surfaces. Gen1 dataflows use the Power BI REST API; Gen2 dataflows are Fabric items managed through the generic Items API.
Gen1 vs Gen2 Comparison
| Aspect | Gen1 (Power BI Dataflows) | Gen2 (Fabric Dataflow Gen2) |
|---|---|---|
| API audience | powerbi | Fabric (default) |
| Endpoint prefix | groups/<ws-id>/dataflows | workspaces/<ws-id>/items |
| Storage | Internal PBI storage or ADLS Gen2 | Lakehouse, Warehouse, Azure SQL, etc. |
| Definition format | model.json (CDM) | dataflow-content.json (base64 mashup) |
| Refresh model | Transaction-based (transactionId) | Job-based (jobInstanceId) |
| CI/CD | Export/Import JSON | Git integration + deployment pipelines |
| Admin APIs | Dedicated /admin/dataflows/ | Generic Fabric admin item APIs |
| Multiple destinations | No | Yes |
| Monitoring Hub | No | Yes |
| Incremental refresh | Premium only | Yes |
---
Gen1 Operations
All Gen1 endpoints require the -A powerbi audience flag and operate under the groups/<ws-id>/dataflows path.
List Dataflows
WS_ID="<workspace-id>"
fab api -A powerbi "groups/$WS_ID/dataflows"Returns an array of objects with objectId, name, description, modelUrl, and configuredBy.
Filter to names only:
fab api -A powerbi "groups/$WS_ID/dataflows" -q "value[].{id:objectId, name:name}"Get Dataflow Definition
Retrieve the full CDM model.json definition for a dataflow.
DF_ID="<dataflow-id>"
fab api -A powerbi "groups/$WS_ID/dataflows/$DF_ID"The response is the raw JSON definition (CDM format), not a wrapper object.
Update Dataflow Properties
fab api -A powerbi -X patch "groups/$WS_ID/dataflows/$DF_ID" -i '{
"name": "Renamed Dataflow",
"description": "Updated description",
"allowNativeQueries": true,
"computeEngineBehavior": "computeOptimized"
}'Valid computeEngineBehavior values: computeOptimized, computeOn, computeDisabled.
Delete Dataflow
fab api -A powerbi -X delete "groups/$WS_ID/dataflows/$DF_ID"Get Data Sources
fab api -A powerbi "groups/$WS_ID/dataflows/$DF_ID/datasources"Returns data source type, gateway ID, and connection details (server, database, URL) for each source.
Trigger Refresh
fab api -A powerbi -X post "groups/$WS_ID/dataflows/$DF_ID/refreshes" -i '{
"notifyOption": "NoNotification"
}'Valid notifyOption values: NoNotification, MailOnFailure. Note that MailOnCompletion is not supported for dataflows.
Optionally append a process type: groups/$WS_ID/dataflows/$DF_ID/refreshes?processType=default.
Get Transactions (Refresh History)
fab api -A powerbi "groups/$WS_ID/dataflows/$DF_ID/transactions"Returns an array of transaction objects with id (the transactionId), refreshType, startTime, endTime, and status.
Cancel a Transaction
Note the path structure -- the cancel endpoint sits under /dataflows/transactions/, not under a specific dataflow.
TX_ID="<transaction-id>"
fab api -A powerbi -X post "groups/$WS_ID/dataflows/transactions/$TX_ID/cancel"Returns { transactionId, status }. Status values: successfullyMarked, alreadyConcluded, invalid, notFound.
Get Upstream Dataflows
Identify dataflows that the target depends on (linked/computed entities).
fab api -A powerbi "groups/$WS_ID/dataflows/$DF_ID/upstreamDataflows"Returns { value: [{ targetDataflowId, groupId }] }.
Update Refresh Schedule
fab api -A powerbi -X patch "groups/$WS_ID/dataflows/$DF_ID/refreshSchedule" -i '{
"value": {
"days": ["Monday", "Wednesday", "Friday"],
"times": ["07:00", "13:00", "19:00"],
"enabled": true,
"localTimeZoneId": "UTC",
"notifyOption": "NoNotification"
}
}'Day values: Sunday through Saturday. Times use 24-hour HH:mm format.
Migrate Gen1 to Gen2
Convert a Gen1 dataflow to a Gen2 (Fabric) artifact. This is a preview feature.
fab api -A powerbi -X post "groups/$WS_ID/dataflows/$DF_ID/saveAsNativeArtifact" -i '{
"displayName": "Migrated Dataflow",
"description": "Converted from Gen1",
"includeSchedule": true,
"targetWorkspaceId": "<target-workspace-id>"
}'Key behavior:
- The original Gen1 dataflow is preserved; a new Gen2 item is created
- Connections are migrated to Fabric format
- If
includeScheduleis true, the schedule is copied in a disabled state - Non-fatal errors appear in the response
errors[]array:FailedToCopySchedule,SetDataflowOriginFailed,ConnectionsUpdateFailed
---
Gen2 Operations
Gen2 dataflows are standard Fabric items of type Dataflow. All operations use the default Fabric audience (no -A flag needed).
List Dataflows
WS_ID="<workspace-id>"
fab api "workspaces/$WS_ID/items?type=Dataflow"Returns standard item objects with id, type, displayName, description, and workspaceId.
Filter to names:
fab api "workspaces/$WS_ID/items?type=Dataflow" -q "[].{id:id, name:displayName}"Get Dataflow Properties
ITEM_ID="<item-id>"
fab api "workspaces/$WS_ID/items/$ITEM_ID"Get Dataflow Definition
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/getDefinition"Returns { definition: { parts: [{ path, payload, payloadType }] } }. Common parts include dataflow-content.json, .platform, queryMetadata.json, and mashup.pq. Payloads are base64-encoded.
Decode a specific part:
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/getDefinition" \
-q "definition.parts[?path=='dataflow-content.json'].payload | [0]" \
| base64 -d | jq .Create Dataflow
Create a dataflow with metadata only (define content in the UI or via update):
fab api -X post "workspaces/$WS_ID/items" -i '{
"displayName": "New Dataflow",
"description": "ETL pipeline for sales data",
"type": "Dataflow"
}'Create with an inline definition by including definition.parts[] containing a base64-encoded dataflow-content.json. The mashup document holds Power Query M code, host properties, and connection overrides.
Update Properties
fab api -X patch "workspaces/$WS_ID/items/$ITEM_ID" -i '{
"displayName": "Renamed Dataflow",
"description": "Updated description"
}'Update Definition
Push a new definition (Power Query M, connections, destinations):
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/updateDefinition" -i '{
"definition": {
"parts": [
{
"path": "dataflow-content.json",
"payload": "<base64-encoded-content>",
"payloadType": "InlineBase64"
}
]
}
}'After updating the definition, a Publish job must run before the new definition takes effect on refresh.
Delete Dataflow
fab api -X delete "workspaces/$WS_ID/items/$ITEM_ID"Trigger Publish Job
Publish validates and activates a definition change. Run this after any definition update and before triggering a refresh.
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances?jobType=Publish"Returns 202 Accepted with a job instance object containing jobInstanceId and status.
Trigger Refresh Job
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances?jobType=Refresh"Returns 202 Accepted. The response includes jobInstanceId for monitoring.
Monitor Job Instance
JOB_ID="<job-instance-id>"
fab api "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances/$JOB_ID"Returns job metadata: id, itemId, jobType, invokeType, status, rootActivityId, startTimeUtc, endTimeUtc, failureReason.
Status progression: Accepted -> Running -> Completed or Failed.
Cancel Job Instance
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances/$JOB_ID/cancel"Returns a Location header and Retry-After: 60.
---
Gen2 Lifecycle
The Gen2 dataflow lifecycle follows three stages:
1. Definition -- Create or update the dataflow content (Power Query M, connections, destinations) 2. Publish -- Validate and activate the definition via a Publish job 3. Refresh -- Execute the ETL via a Refresh job
This differs from Gen1, where calling the refresh endpoint directly is sufficient. In Gen2, skipping the Publish step after a definition change causes the refresh to run against the previous definition.
---
Common Workflows
Gen1: Trigger and Monitor Refresh
WS_ID="<workspace-id>"
DF_ID="<dataflow-id>"
# 1. Trigger
fab api -A powerbi -X post "groups/$WS_ID/dataflows/$DF_ID/refreshes" -i '{
"notifyOption": "NoNotification"
}'
# 2. Poll transactions for latest status
fab api -A powerbi "groups/$WS_ID/dataflows/$DF_ID/transactions" \
-q "value | sort_by(@, &startTime) | [-1]"
# 3. Cancel if needed (use transactionId from step 2)
TX_ID="<transaction-id>"
fab api -A powerbi -X post "groups/$WS_ID/dataflows/transactions/$TX_ID/cancel"Gen1 does not return a transaction ID from the trigger call. Poll the transactions endpoint and sort by startTime to find the latest entry. Match on refreshType: "OnDemand" if multiple entries exist.
Gen2: Trigger and Monitor Refresh
WS_ID="<workspace-id>"
ITEM_ID="<item-id>"
# 1. Trigger -- capture jobInstanceId from response
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances?jobType=Refresh" \
-q "id"
# 2. Poll job instance directly
JOB_ID="<job-instance-id>"
fab api "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances/$JOB_ID"
# 3. Cancel if needed
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances/$JOB_ID/cancel"Gen2 returns a jobInstanceId in the trigger response, enabling deterministic polling without timestamp sorting.
Gen2: Update Definition, Publish, and Refresh
# 1. Push new definition
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/updateDefinition" -i '{
"definition": {
"parts": [
{
"path": "dataflow-content.json",
"payload": "<base64>",
"payloadType": "InlineBase64"
}
]
}
}'
# 2. Publish
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances?jobType=Publish" -q "id"
# 3. Wait for publish to complete, then refresh
PUBLISH_JOB="<publish-job-id>"
fab api "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances/$PUBLISH_JOB"
# When status is "Completed":
fab api -X post "workspaces/$WS_ID/items/$ITEM_ID/jobs/instances?jobType=Refresh"---
Admin Endpoints
Gen1 Admin: Tenant-Wide Inventory
All Gen1 admin endpoints require the Fabric Admin role or a service principal with Tenant.Read.All / Tenant.ReadWrite.All. Rate limit: 200 requests per hour.
List all Gen1 dataflows across the tenant:
fab api -A powerbi "admin/dataflows?\$top=5000"Paginate with $skip:
fab api -A powerbi "admin/dataflows?\$top=5000&\$skip=5000"List dataflows in a specific workspace (admin context):
fab api -A powerbi "admin/groups/$WS_ID/dataflows"Gen1 Admin: Dataflow Audit
Retrieve data sources, users, definition, and upstream dependencies without workspace membership:
DF_ID="<dataflow-id>"
# Data sources
fab api -A powerbi "admin/dataflows/$DF_ID/datasources"
# Access permissions
fab api -A powerbi "admin/dataflows/$DF_ID/users"
# Full definition export
fab api -A powerbi "admin/dataflows/$DF_ID/export"
# Upstream dependencies (requires workspace ID)
fab api -A powerbi "admin/groups/$WS_ID/dataflows/$DF_ID/upstreamDataflows"The users endpoint returns dataflowUserAccessRight values: None, Read, ReadWrite, ReadReshare, Owner.
Note: The datasources admin endpoint may return deleted data sources in the response.
Gen2 Admin: Fabric Item APIs
Gen2 dataflows appear as Fabric items. Use the generic admin item endpoints:
fab api "admin/workspaces/$WS_ID/items?type=Dataflow"---
Permissions
Gen1 Scopes
| Operation | Required Scope |
|---|---|
| List, get, datasources, transactions, upstream | Dataflow.Read.All or Dataflow.ReadWrite.All |
| Delete, update, refresh, cancel, schedule | Dataflow.ReadWrite.All |
| All admin endpoints | Tenant.Read.All or Tenant.ReadWrite.All |
Gen2 Scopes
Gen2 dataflows use Fabric item-level scopes: Workspace.ReadWrite.All or Item.ReadWrite.All.
---
Limitations
Gen1
MailOnCompletionnotification is not supported -- onlyNoNotificationandMailOnFailure- Computed and linked entities require Premium capacity
- DirectQuery via the dataflow connector requires Premium capacity
- No native Git integration or Monitoring Hub support
- Refresh triggers return no transaction ID; identify the active transaction by polling and sorting by timestamp
Gen2
- Service principal authentication is not currently supported for Gen2 dataflow APIs
Get ItemandList Item Access Detailsmay not return correct information when filtering on the Dataflow type- The Publish job must complete before a Refresh job reflects definition changes
- Gen2 notifications are not available via the API -- use the Monitoring Hub instead
- Check current documentation for updates on Run API reliability; earlier previews had limitations where triggered runs would accept but not execute
Deployment Pipelines
fab deploy is the first-class CI/CD command and wraps the fabric-cicd Python library; use it for environment promotion (dev to test to prod) when fabric-cicd's defaults fit. Outside that path all operations go through fab api. Two API surfaces coexist:
| Aspect | Fabric API (default) | Power BI API (-A powerbi) |
|---|---|---|
| Endpoint prefix | deploymentPipelines | pipelines |
| Item scope | All Fabric + Power BI items | Power BI items only (reports, dashboards, semantic models, dataflows, datamarts) |
| Stage addressing | Stage UUID | Stage order integer (0 = first stage) |
| Deploy endpoints | Single deploy (full or selective) | Separate deployAll and deploy (selective) |
| Extra deploy options | allowCrossRegionDeployment | allowPurgeData, allowTakeOver, allowSkipTilesWithMissingPrerequisites, allowOverwriteArtifact, allowCreateArtifact, allowOverwriteTargetArtifactLabel, updateAppSettings |
| Role model | RBAC role assignments (Admin only) | users endpoint with accessRight |
When to use which:
- Use
fab deployfor routine environment promotion; it owns the fabric-cicd integration and replaces hand-rolled deploy scripts for most cases. - Default to the Fabric API for low-level pipeline control; it covers Fabric items (Lakehouse, Notebook, Warehouse, etc.) and Power BI items alike.
- Fall back to the Power BI API only when you need per-item deploy options (
allowPurgeData,allowTakeOver,allowSkipTilesWithMissingPrerequisites) or want to refresh the workspace app viaupdateAppSettings.
---
Command reference
| Command | Purpose | Example |
|---|---|---|
fab api "deploymentPipelines" | List pipelines | fab api "deploymentPipelines" -q "value[].{name:displayName,id:id}" |
fab api "deploymentPipelines/<id>" | Get pipeline + stages | fab api "deploymentPipelines/$PIPELINE_ID" |
fab api -X post "deploymentPipelines" | Create pipeline | see Create |
fab api -X patch "deploymentPipelines/<id>" | Rename / describe | see Update |
fab api -X delete "deploymentPipelines/<id>" | Delete pipeline | fab api -X delete "deploymentPipelines/$PIPELINE_ID" |
fab api ".../stages" | List stages | fab api "deploymentPipelines/$PIPELINE_ID/stages" |
fab api -X post ".../assignWorkspace" | Assign workspace | see Assign |
fab api -X post ".../unassignWorkspace" | Unassign workspace | see Unassign |
fab api ".../stages/<id>/items" | List items in stage | fab api "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID/items" |
fab api -X post ".../deploy" | Promote content | see Deploying |
fab api ".../operations" | Deployment history | fab api "deploymentPipelines/$PIPELINE_ID/operations" |
fab api "operations/<op-id>" | Poll LRO status | fab api "operations/$OPERATION_ID" |
fab api ".../roleAssignments" | Pipeline access | see Access |
Flags (common):
-X post|patch|delete(HTTP method)-i '<json>'(request body; pass a file path for larger payloads)-q "<jmespath>"(filter response)-A powerbi(switch to Power BI API surface)--show_headers(surfacex-ms-operation-idandLocationfor LROs)
---
Pipeline CRUD
Create a pipeline
Stage count (2...10) and order are locked at creation. Only displayName, description, and per-stage isPublic can change later; stages cannot be added, removed, or reordered after the first request returns.
fab api -X post "deploymentPipelines" -i '{
"displayName": "Sales Pipeline",
"description": "Dev, Test, Prod for sales content",
"stages": [
{ "displayName": "Development", "isPublic": false },
{ "displayName": "Test", "isPublic": false },
{ "displayName": "Production", "isPublic": true }
]
}'Response (201) contains the pipeline ID and the generated stage IDs / order numbers. Capture these before any further calls.
Read a pipeline
# Whole pipeline (metadata + stages + workspace assignments)
fab api "deploymentPipelines/$PIPELINE_ID"
# Single stage
fab api "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID"
# Find a pipeline by name
PIPELINE_ID=$(fab api "deploymentPipelines" \
-q "value[?displayName=='Sales Pipeline'].id | [0]" | tr -d '"')
# Stage IDs by order
DEV_STAGE=$(fab api "deploymentPipelines/$PIPELINE_ID/stages" \
-q "value[?order==\`0\`].id | [0]" | tr -d '"')Admins can list all pipelines in the tenant via the Power BI API: fab api -A powerbi "admin/pipelines".
Update pipeline metadata
# Rename / redescribe
fab api -X patch "deploymentPipelines/$PIPELINE_ID" -i '{
"displayName": "Sales Pipeline (FY27)",
"description": "Updated for FY27 dataflow split"
}'
# Per-stage edits (isPublic, description)
fab api -X patch "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID" -i '{
"description": "Consumer-facing production stage",
"isPublic": true
}'To add or remove stages, you must create a new pipeline, reassign workspaces to it, then delete the old one. There is no in-place reshape.
Delete a pipeline
fab api -X delete "deploymentPipelines/$PIPELINE_ID"Fails (409) while an operation is in progress. Unassigning all workspaces first is not required but makes intent explicit.
---
Stage and workspace assignment
Assign a workspace to a stage
Requirements:
- Stage must not already have an assigned workspace.
- Workspace must not be assigned to any other pipeline stage.
- Caller must be Admin on the pipeline AND Admin on the workspace.
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID/assignWorkspace" \
-i '{"workspaceId": "<workspace-id>"}'Unassign a workspace from a stage
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID/unassignWorkspace"Fails if a deployment is in progress on that pipeline. Item pairing metadata is dropped; re-assigning a workspace creates a fresh pairing on next compare or deploy.
Swap a workspace between stages
There is no native "move workspace between stages" operation. To swap, unassign then re-assign:
# 1. Unassign from the current stage
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$OLD_STAGE/unassignWorkspace"
# 2. Assign to the target stage
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$NEW_STAGE/assignWorkspace" \
-i "{\"workspaceId\": \"$WORKSPACE_ID\"}"This resets item pairings for that workspace. Deploying afterwards re-pairs items by type + name + folder path.
List items in a stage
fab api "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID/items" \
-q "value[].{name:itemDisplayName, type:itemType, sourceId:sourceItemId, targetId:targetItemId, lastDeployed:lastDeploymentTime}"sourceItemId is the item's ID in this stage; targetItemId is its paired ID in the next stage (if any). These IDs are what selective deploys reference.
---
Access management
Two role models coexist. Prefer the Fabric RBAC endpoint; it's the forward direction.
Fabric RBAC (role assignments)
Only the Admin role exists today.
# List current assignments
fab api "deploymentPipelines/$PIPELINE_ID/roleAssignments"
# Add user
fab api -X post "deploymentPipelines/$PIPELINE_ID/roleAssignments" -i '{
"principal": { "id": "<user-object-id>", "type": "User" },
"role": "Admin"
}'
# Add service principal
fab api -X post "deploymentPipelines/$PIPELINE_ID/roleAssignments" -i '{
"principal": { "id": "<sp-object-id>", "type": "ServicePrincipal" },
"role": "Admin"
}'
# Add security group (M365 groups are NOT supported)
fab api -X post "deploymentPipelines/$PIPELINE_ID/roleAssignments" -i '{
"principal": { "id": "<group-object-id>", "type": "Group" },
"role": "Admin"
}'
# Remove
fab api -X delete "deploymentPipelines/$PIPELINE_ID/roleAssignments/$ROLE_ASSIGNMENT_ID"Power BI users endpoint
fab api -A powerbi "pipelines/$PIPELINE_ID/users"
fab api -A powerbi -X post "pipelines/$PIPELINE_ID/users" -i '{
"identifier": "user@contoso.com",
"accessRight": "Admin",
"principalType": "User"
}'
fab api -A powerbi -X delete "pipelines/$PIPELINE_ID/users/user@contoso.com"Restrict pipeline admin to release managers and technical owners. Pipeline admin alone grants no access to workspace content; the caller still needs a workspace role to deploy or compare stages.
---
Deploying content
Deployment is an LRO. The deploy endpoint returns 202 Accepted with x-ms-operation-id and Location headers; use those to poll.
Full deploy (all items)
Omit the items array to push everything from source to target.
fab api -X post "deploymentPipelines/$PIPELINE_ID/deploy" -i "{
\"sourceStageId\": \"$SOURCE_STAGE\",
\"targetStageId\": \"$TARGET_STAGE\",
\"note\": \"Full promote Dev to Test\"
}" --show_headersSelective deploy (individual items)
Promote one or a handful of items; the Fabric API flattens them into a single array. Max 300 items per request.
fab api -X post "deploymentPipelines/$PIPELINE_ID/deploy" -i "{
\"sourceStageId\": \"$SOURCE_STAGE\",
\"targetStageId\": \"$TARGET_STAGE\",
\"items\": [
{ \"sourceItemId\": \"<semantic-model-id>\", \"itemType\": \"SemanticModel\" },
{ \"sourceItemId\": \"<report-id>\", \"itemType\": \"Report\" }
],
\"note\": \"Selective; model + single report\"
}"Pick sourceItemId values from stages/<id>/items. Dependent items must already exist in the target stage or be included in the same call; otherwise the deployment fails. There is no selectRelated flag via the API; you have to resolve dependencies yourself.
Deploy to an empty stage (create target workspace)
When the target stage has no workspace, provide createdWorkspaceDetails and the service provisions one.
fab api -X post "deploymentPipelines/$PIPELINE_ID/deploy" -i "{
\"sourceStageId\": \"$SOURCE_STAGE\",
\"targetStageId\": \"$TARGET_STAGE\",
\"createdWorkspaceDetails\": {
\"name\": \"Sales-Prod\",
\"capacityId\": \"<capacity-id>\"
},
\"note\": \"Initial promote to Prod\"
}"capacityId is optional; omit it to let the service auto-pick.
Backward deployment
Deploying from a later stage back to an earlier one (e.g. Prod to Test) is only supported when the target stage is empty (no workspace assigned). Use the createdWorkspaceDetails pattern above, or unassign the target first. The Power BI API exposes an explicit isBackwardDeployment flag; the Fabric API infers it.
Cross-region deploy (Fabric API)
fab api -X post "deploymentPipelines/$PIPELINE_ID/deploy" -i "{
\"sourceStageId\": \"$SOURCE_STAGE\",
\"targetStageId\": \"$TARGET_STAGE\",
\"options\": { \"allowCrossRegionDeployment\": true },
\"note\": \"Cross-region promote\"
}"Power BI API deploys (when you need the extra options)
# Full deploy with PBI-specific options
fab api -A powerbi -X post "pipelines/$PIPELINE_ID/deployAll" -i '{
"sourceStageOrder": 0,
"options": {
"allowOverwriteArtifact": true,
"allowCreateArtifact": true,
"allowPurgeData": false,
"allowTakeOver": false,
"allowSkipTilesWithMissingPrerequisites": false
},
"updateAppSettings": { "updateAppInTargetWorkspace": true },
"note": "Deploy all; refresh Prod app"
}'
# Selective deploy; typed arrays instead of a flat items array
fab api -A powerbi -X post "pipelines/$PIPELINE_ID/deploy" -i '{
"sourceStageOrder": 0,
"datasets": [
{ "sourceId": "<dataset-id>", "options": { "allowOverwriteArtifact": true } }
],
"reports": [
{ "sourceId": "<report-id>" }
],
"options": { "allowCreateArtifact": true, "allowOverwriteArtifact": true }
}'---
Reviewing diffs and deployment history
Always compare before you deploy, then capture the operation for audit afterwards.
Pre-deployment diff
The Fabric API returns per-item diff state inside the operation's execution plan. To preview a diff without deploying, either compare stage item lists directly or (simpler) dry-run in the portal.
# Flat list comparison via items endpoints
fab api "deploymentPipelines/$PIPELINE_ID/stages/$DEV_STAGE/items" -o /tmp/dev.json
fab api "deploymentPipelines/$PIPELINE_ID/stages/$TEST_STAGE/items" -o /tmp/test.json
diff \
<(jq -S 'sort_by(.itemDisplayName)' /tmp/dev.json) \
<(jq -S 'sort_by(.itemDisplayName)' /tmp/test.json)Pairing is created on first deploy or workspace assignment. Items pair on type + display name; if multiple items share a name, the folder path has to match too. Moving or renaming folders breaks pairing and surfaces items as Different.
Post-deploy execution plan
After the deploy call returns, fetch the detailed execution plan; it contains one step per item with diff state and error info:
fab api "deploymentPipelines/$PIPELINE_ID/operations/$OPERATION_ID" \
-q "executionPlan.steps[].{
item: description,
status: status,
diff: preDeploymentDiffState
}"preDeploymentDiffState values:
New; the item exists only in the source stage and was clonedDifferent; paired items differ; the source overwrote the targetNoDifference; nothing changed (still logged for audit)
preDeploymentDiffInformation on the operation root contains the roll-up counts. The portal's Compare view renders the same data as New, Different from source, Only in source, Same as source, and Not in source (items that exist only in the target and are left untouched).
LRO polling
OPERATION_ID="<x-ms-operation-id header>"
fab api "operations/$OPERATION_ID" \
-q "{status:status, pct:percentComplete, updated:lastUpdatedTimeUtc}"status cycles through NotStarted, Running, then terminates at Succeeded or Failed. Polling loop:
while true; do
STATUS=$(fab api "operations/$OPERATION_ID" -q "status" | tr -d '"')
echo "status: $STATUS"
[ "$STATUS" = "Succeeded" ] || [ "$STATUS" = "Failed" ] && break
sleep 30
doneThe LRO result is retained for 24 hours after completion; fetch it promptly for audit purposes via fab api "operations/$OPERATION_ID/result".
Deployment history
Returns up to the last 20 operations with status, type (Deploy, AssignWorkspace, etc.), who triggered them, and the attached note.
# Fabric API
fab api "deploymentPipelines/$PIPELINE_ID/operations" \
-q "value[].{time:executionStartTime, type:type, status:status, note:note, by:performedBy}"
# Power BI API (equivalent)
fab api -A powerbi "pipelines/$PIPELINE_ID/operations"Review history regularly when multiple admins can deploy; it's the primary audit trail for spotting unapproved or failed promotes.
---
Deployment rules
Rules rewrite item configuration during deployment so each stage can point at its own data source, parameter, or lakehouse. They're configured on the target stage and apply every time the paired item is deployed into that stage.
| Item type | Data source rule | Parameter rule | Default lakehouse rule |
|---|---|---|---|
| Semantic model | yes | yes | ; |
| Dataflow Gen1 | yes | yes | ; |
| Paginated report | yes | ; | ; |
| Mirrored database | yes | ; | ; |
| Notebook | ; | ; | yes |
Rules cannot be managed with a clean native endpoint in the Fabric API; in practice they're set in the portal or via the Power BI pipelines updatePipelineConfiguration endpoint. When you need automated rule management at scale, fab api -A powerbi -X post "pipelines/<id>/stages/<order>/rules" with a JSON body matching the Power BI REST schema is the supported path.
Caveats:
- The rule owner must be the item owner AND at least a contributor on the target workspace.
- Data source rules only work when swapping between data sources of the same type.
- Rules applied to semantic models flag paired items as
Differentuntil you deploy (because the rule hasn't been materialised yet). - Parameters used for rule-based rebinding must be of type
Text.
---
Content lifecycle management patterns
These patterns are distilled from Microsoft's Power BI implementation planning series on content lifecycle management. Deployment pipelines serve one stage of a broader lifecycle; they aren't a substitute for source control or testing.
The six lifecycle stages
Power BI content moves through plan/design, develop, validate, deploy, support/monitor, and retire/archive. Deployment pipelines primarily serve deploy, with a side role in validate through the Compare view.
Workspace strategy
Separate workspaces by environment and optionally by item type. Each workspace maps to exactly one deployment pipeline stage.
Single pipeline ; the common case.
All content in one workspace per stage, one pipeline.
Dev Workspace ... Test Workspace ... Prod Workspace
stage 0 stage 1 stage 2Multiple linked pipelines ; separating by item type.
When you split content across workspaces by item type (e.g. a data workspace holding semantic models, lakehouses, and dataflows; a reporting workspace holding reports, dashboards, and paginated reports), you need one pipeline per workspace. These pipelines link automatically through cross-pipeline auto-binding, so a report in the Test stage of the reporting pipeline stays connected to the semantic model in the Test stage of the data pipeline after every deploy.
Pipeline A (Data): Dev-Data ... Test-Data ... Prod-Data
| | |
auto-binds auto-binds auto-binds
| | |
Pipeline B (Reports): Dev-Rpt ... Test-Rpt ... Prod-RptRequirements for the link to work:
- Both pipelines must have the same number of stages. A 3-stage data pipeline cannot link to a 4-stage reporting pipeline.
- Stage order matters, not stage name. Pipeline A stage 0 binds to Pipeline B stage 0 regardless of what they're called.
- The dependency must already exist in the target stage when the dependent item is deployed, or the deploy fails with a lineage error. If you're promoting together, deploy the data pipeline first, then the reporting pipeline, so reports find their freshly promoted models.
- Reports / dashboards stay bound to data items through the auto-bind even when the two pipelines are in different workspaces.
Common topologies:
| Topology | Pipelines | When to use |
|---|---|---|
| Single pipeline, single workspace per stage | 1 | All content owned by one team; simplest case |
| Two linked pipelines (data + reporting) | 2 | Separate data engineering and report authoring teams; different release cadences |
| Three linked pipelines (ingestion + modelling + reporting) | 3 | Medallion-style separation; lakehouse/warehouse in one, semantic models in another, reports in a third |
| Pipeline per domain | N | Federated ownership; each data domain has its own dev/test/prod |
Example cross-pipeline layout with three data domains plus a shared reporting pipeline:
Sales-Data: Dev ... Test ... Prod
Finance-Data: Dev ... Test ... Prod
HR-Data: Dev ... Test ... Prod
| | | (auto-bind per matching stage)
Reports: Dev ... Test ... ProdEach Reports stage can reference models from any of the three data pipelines, as long as the matching stage exists in the source data pipeline. When you want to break auto-binding (e.g. reports should always read from Prod-Data regardless of stage), use one of the opt-outs under Auto-binding behavior.
To link pipelines programmatically, there's no explicit "link" API call; linking is implicit through workspace assignment. You create each pipeline, assign the appropriate workspaces to matching stages, and the binding resolves on the next deploy or compare. Review item lineage after linking to confirm the bindings are correct:
# Find reports and the semantic models they depend on, per workspace
fab api "workspaces/<reporting-ws-id>/items" -q "value[?type=='Report']"
# Use Power BI scanner API or the get-downstream-reports.py script for full lineageSee `scripts/get-downstream-reports.py` for a lineage walker that works across workspaces without admin access.
Auto-binding behavior
Deployment pipelines automatically reconnect deployed items to their dependencies in the target stage:
- Same workspace ; the paired dependency is picked up automatically.
- Across pipelines ; works only when both pipelines have identical stage counts and the dependency already exists in the target stage.
- Missing dependency ; the deploy fails with a lineage error.
When auto-binding is undesirable (e.g. all reports should always point at the Prod semantic model regardless of stage), choose one of:
1. Don't connect the items in the same stage ; pipelines keep the original connection. 2. Define a parameter rule (semantic models and dataflows only; not reports). 3. Connect reports / dashboards to a proxy semantic model that isn't connected to any pipeline.
Deployment approaches
The implementation planning guidance lists five deployment approaches. Pipelines are one of them:
| Approach | Complexity | Best for |
|---|---|---|
| Publish from Power BI Desktop | Lowest | Self-service creators, manual control |
| Publish via XMLA endpoint | Moderate | Tabular Editor users, semantic-model-only work |
| OneDrive refresh | Moderate | Self-service with simple version control |
| Fabric Git integration | Higher | Azure DevOps / GitHub users on Fabric capacity with .pbip files |
| Azure Pipelines (CI/CD) | Highest | Enterprise teams, full automation, custom validation and release approvals |
Deployment pipelines are complementary to the others, not competing. The canonical enterprise pattern is: Git for source control and code review, deployment pipelines for the promote between workspaces, Azure Pipelines to orchestrate both and to run validation + build + release stages with approvals.
Typical Fabric Git branching topology:
devbranch syncs to the dev workspace- Pull request
devtotestpromotes via the test workspace sync - Pull request
testtomainpromotes to prod
Deployment rules in a lifecycle context
Use rules to model environment differences the code doesn't carry:
- Data source rules ; swap Dev DB for Prod DB on each stage.
- Parameter rules ; swap connection strings, feature flags, or row limits per stage.
- Default lakehouse rule ; rebind notebooks to the stage-appropriate lakehouse.
Rules are the right lever when config differs by stage. When everything changes (data, permissions, credentials), you're looking at post-deployment activities, not rules.
Post-deployment activities
Deployment copies definitions only. After a deploy, handle these manually or via automation:
| Activity | Copied during deploy? | Notes |
|---|---|---|
| Item definitions (model, visuals, pages) | yes | ; |
| Data source connections | via rules | Set data source rules per stage |
| Parameters | via rules | Set parameter rules per stage |
| Actual row data | no | Refresh semantic models after deploy |
| Data source credentials | no | Set per stage |
| Scheduled refresh config | no | Configure on target model after first deploy |
| Gateway mappings | no | Configure after first deploy |
| RLS role members | no | Assign per stage |
| Item permissions | no | Manage per stage |
| Sensitivity labels | conditional | Copied on first deploy; later only when source has a protected label and target doesn't |
| Workspace-level settings | no | Each stage has its own workspace |
| Power BI app content | no | Republish per stage (use updateAppSettings via PBI API) |
Trigger a refresh of the promoted model right after deployment so users aren't staring at stale data:
MODEL_ID=$(fab get "Prod.Workspace/Sales.SemanticModel" -q "id" | tr -d '"')
WS_ID=$(fab get "Prod.Workspace" -q "id" | tr -d '"')
fab api -A powerbi -X post "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -i '{"type":"Full"}'Governance
- Deploy in one direction. Dev to Test to Prod. Don't make changes directly in later stages; they'll just get overwritten on the next deploy, or worse, they won't and you'll drift.
- Restrict pipeline admin. Grant it to release managers and technical owners, not to every content creator. Pipeline admin plus workspace admin is the combination that can deploy.
- Review deployment history regularly. It's your audit trail for unapproved or failed promotes. When auto-binding is in play, also review item lineage to catch broken bindings from someone publishing to the wrong stage.
- Use the Compare view before deploy. Especially when you don't have a Git remote for source control; the diff is the only record of what's about to change.
- Always attach a note. The
notefield on the deploy call shows up in deployment history; treat it like a commit message. - Release approvals. When orchestrating deploys with Azure Pipelines, require explicit sign-off from a release manager for test and prod stages.
---
Permissions summary
Pipeline + workspace role matrix
| Action | Pipeline role | Workspace role |
|---|---|---|
| View list of pipelines | ; (free user) | ; |
| Create a pipeline | Licensed user (Pro / PPU / Premium) | ; |
| View pipeline metadata / stages | Admin | ; |
| Share, edit, delete pipeline | Admin | ; |
| Assign workspace to a stage | Admin | Workspace admin |
| Unassign workspace from a stage | Admin | ; (or Workspace admin via PBI unassign API) |
| View items in a stage | Admin | Workspace reader+ |
| Compare two stages | Admin | Contributor, member, or admin on both |
| Deploy to an empty stage | Admin | Contributor on source |
| Deploy to an existing stage | Admin | Contributor on source AND target |
| View or set a deployment rule | Admin | Contributor+ on target AND item owner |
| View deployment history | Admin | ; |
| Manage role assignments | Admin | ; |
Extra item-specific twists:
- Dataflows ; the deployer must be the dataflow owner.
- Semantic models ; if the tenant admin switch "block republish and disable package refresh" is on, only the model owner can deploy updates to it.
- GCC environment ; deployers need workspace Member on both stages (Contributor isn't enough).
Required delegated scopes
| Action | Scope |
|---|---|
| Read pipelines, stages, items, operations | Pipeline.Read.All or Pipeline.ReadWrite.All |
| Create, update, delete pipelines | Pipeline.ReadWrite.All |
| Assign / unassign workspace | Pipeline.ReadWrite.All + Workspace.ReadWrite.All |
| Deploy content | Pipeline.Deploy |
| Manage role assignments | Pipeline.ReadWrite.All |
Identity support
User principals, service principals, managed identities, and SPN profiles all work across every endpoint. Service principal automation additionally requires the Fabric admin setting "Service principals can create workspaces, connections, and deployment pipelines" to be enabled. Microsoft 365 groups are not supported as pipeline admins; use a security group instead.
---
Supported item types (Fabric API)
The Fabric deploy endpoint accepts these itemType values:
Dashboard, Report, SemanticModel, PaginatedReport, Datamart, Lakehouse, Eventhouse, Environment, KQLDatabase, KQLQueryset, KQLDashboard, DataPipeline, Notebook, SparkJobDefinition, MLExperiment, MLModel, Warehouse, Eventstream, SQLEndpoint, MirroredWarehouse, MirroredDatabase, Reflex, GraphQLApi, SQLDatabase, CopyJob, VariableLibrary, Dataflow.
The Power BI selective deploy uses typed arrays instead: datasets, reports, dashboards, dataflows, datamarts.
---
Limitations and gotchas
- Stage count is permanent. 2...10 stages, fixed at creation. You can rename them or toggle
isPublic; you cannot add, remove, or reorder. - Max 300 items per deploy request.
- No concurrent deployments on the same pipeline. Delete also fails while a deploy is running.
- Backward deploy needs an empty target. Unassign the target workspace first, or use
createdWorkspaceDetailsto provision a new one. - No data copy. Deployment carries definitions only; refresh semantic models after the promote.
- Item identity preserved. IDs, URLs, and permissions in the target stage survive overwrite.
- Gateway mappings aren't configured after the first deploy; script them separately.
- Fabric API gaps.
allowPurgeData,allowTakeOver,allowSkipTilesWithMissingPrerequisites, andupdateAppSettingsare Power BI API only. - Empty folders aren't deployed. Folder hierarchy changes ship on deploy, not on workspace assignment.
- Direct Lake semantic models don't auto-rebind to target-stage lakehouses; use a data source rule (or post-deploy rebind) to fix this.
- Incremental refresh policy copies cleanly; existing partitions and data are preserved on the target model. Gen1 dataflow refresh settings don't copy.
- Sensitivity labels copy on first deploy only, or when the source has a protected label and the target doesn't.
- Semantic model ownership. First deploy transfers ownership to the deployer; subsequent deploys leave it alone.
- PBIR reports. Microsoft's docs list PBIR as unsupported; in practice PBIR reports deploy through the Fabric API (verified March 2026). Treat this as undocumented and validate per-tenant before relying on it.
- Real-time connectivity semantic models, DQ / Composite models using auto date/time or variation tables, and datasets with circular dependencies all refuse to deploy.
- LRO results expire 24 hours after completion. Capture them into your audit log promptly.
- `.pbix` download from a stage that was populated by deployment is not supported; export from Desktop or via
fab exportinstead.
---
Full end-to-end workflow
# 1. Create the pipeline with three stages
fab api -X post "deploymentPipelines" -i '{
"displayName": "Sales Pipeline",
"stages": [
{ "displayName": "Development" },
{ "displayName": "Test" },
{ "displayName": "Production" }
]
}'
# Capture PIPELINE_ID, DEV_STAGE, TEST_STAGE, PROD_STAGE from the response
# 2. Assign workspaces
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$DEV_STAGE/assignWorkspace" \
-i '{"workspaceId": "<dev-ws-id>"}'
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$TEST_STAGE/assignWorkspace" \
-i '{"workspaceId": "<test-ws-id>"}'
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$PROD_STAGE/assignWorkspace" \
-i '{"workspaceId": "<prod-ws-id>"}'
# 3. Compare Dev and Test before deploying
fab api "deploymentPipelines/$PIPELINE_ID/stages/$DEV_STAGE/items" -o /tmp/dev.json
fab api "deploymentPipelines/$PIPELINE_ID/stages/$TEST_STAGE/items" -o /tmp/test.json
diff <(jq -S 'sort_by(.itemDisplayName)' /tmp/dev.json) \
<(jq -S 'sort_by(.itemDisplayName)' /tmp/test.json)
# 4. Promote Dev to Test
fab api -X post "deploymentPipelines/$PIPELINE_ID/deploy" -i "{
\"sourceStageId\": \"$DEV_STAGE\",
\"targetStageId\": \"$TEST_STAGE\",
\"note\": \"Initial promote to Test\"
}" --show_headers
# Capture OPERATION_ID from x-ms-operation-id
# 5. Poll until complete
while true; do
STATUS=$(fab api "operations/$OPERATION_ID" -q "status" | tr -d '"')
[ "$STATUS" = "Succeeded" ] || [ "$STATUS" = "Failed" ] && break
sleep 30
done
# 6. On failure, inspect the execution plan
fab api "deploymentPipelines/$PIPELINE_ID/operations/$OPERATION_ID" \
-q "executionPlan.steps[?status=='Failed'].{item:description, error:error}"
# 7. Refresh the promoted model
MODEL_ID=$(fab get "TestWorkspace.Workspace/Sales.SemanticModel" -q "id" | tr -d '"')
WS_ID=$(fab get "TestWorkspace.Workspace" -q "id" | tr -d '"')
fab api -A powerbi -X post "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -i '{"type":"Full"}'---
External references
- Deployment process overview: understand-the-deployment-process
- Compare content between stages: compare-pipeline-content
- Deployment history: deployment-history
- Deployment rules: create-rules
- Pipeline automation with Fabric APIs: pipeline-automation-fabric
- Pipeline automation with Power BI APIs: pipeline-automation
- Content lifecycle management overview: powerbi-implementation-planning-content-lifecycle-management-overview
- Content lifecycle management; deploy content: powerbi-implementation-planning-content-lifecycle-management-deploy
- Self-service content publishing usage scenario: powerbi-implementation-planning-usage-scenario-self-service-content-publishing
- Enterprise content publishing usage scenario: powerbi-implementation-planning-usage-scenario-enterprise-content-publishing
- Fabric REST API ; Deployment Pipelines: rest/api/fabric/core/deployment-pipelines
- Power BI REST API ; Pipelines: rest/api/power-bi/pipelines
Fabric API Reference
Direct API access via fab api for operations beyond standard commands.
Note: Several operations previously requiringfab apinow have native commands. Checkfab acl(permissions),fab assign/unassign(capacity/domain),fab start/stop(capacities),fab ls -q(filtered listing), andfab label(sensitivity labels) before using API calls.
API Basics
# Fabric API (default)
fab api "<endpoint>"
# Power BI API
fab api -A powerbi "<endpoint>"
# With query
fab api "<endpoint>" -q "value[0].id"
# POST with body
fab api -X post "<endpoint>" -i '{"key":"value"}'Capacities
# Native alternatives (preferred):
# fab ls .capacities # List capacities
# fab start .capacities/X.Capacity # Start capacity
# fab stop .capacities/X.Capacity # Stop capacity
# List all capacities
fab api capacities
# Response includes: id, displayName, sku (F2, F64, FT1, PP3), region, statePause/resume capacity (cost savings):
# CAUTION: Pausing stops all workloads on that capacity
# Native (preferred):
fab stop .capacities/MyCapacity.Capacity -f # Pause
fab start .capacities/MyCapacity.Capacity # Resume
# Via Azure CLI (if fab CLI not available):
az resource update --ids "/subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Fabric/capacities/{name}" \
--set properties.state=PausedGateways
# List gateways
fab api -A powerbi gateways
# Get gateway datasources
GATEWAY_ID="<gateway-id>"
fab api -A powerbi "gateways/$GATEWAY_ID/datasources"
# Get gateway users
fab api -A powerbi "gateways/$GATEWAY_ID/users"
# For gateway permissions, use native:
# fab acl ls .gateways/gw.Gateway
# fab acl set .gateways/gw.Gateway -I <objectId> -R ConnectionCreatorDeployment Pipelines
# List pipelines (user)
fab api -A powerbi pipelines
# List pipelines (admin - all tenant)
fab api -A powerbi admin/pipelines
# Get pipeline stages
PIPELINE_ID="<pipeline-id>"
fab api -A powerbi "pipelines/$PIPELINE_ID/stages"
# Get pipeline operations
fab api -A powerbi "pipelines/$PIPELINE_ID/operations"Deploy content (use Fabric API):
# Note: fab assign handles capacity/domain assignment natively,
# but deployment pipelines still require the API.
# Assign workspace to stage
fab api -X post "deploymentPipelines/$PIPELINE_ID/stages/$STAGE_ID/assignWorkspace" \
-i '{"workspaceId":"<workspace-id>"}'
# Deploy to next stage
fab api -X post "deploymentPipelines/$PIPELINE_ID/deploy" -i '{
"sourceStageOrder": 0,
"targetStageOrder": 1,
"options": {"allowCreateArtifact": true, "allowOverwriteArtifact": true}
}'Domains
# List domains
fab api admin/domains
# Get domain workspaces
DOMAIN_ID="<domain-id>"
fab api "admin/domains/$DOMAIN_ID/workspaces"
# Assign workspaces to domain
fab api -X post "admin/domains/$DOMAIN_ID/assignWorkspaces" \
-i '{"workspacesIds":["<ws-id-1>","<ws-id-2>"]}'
# Native alternative (preferred):
# fab assign .domains/domain.Domain -W ws.Workspace -fDataflows
Gen1 (Power BI dataflows):
# List all dataflows (admin)
fab api -A powerbi admin/dataflows
# List workspace dataflows
WS_ID="<workspace-id>"
fab api -A powerbi "groups/$WS_ID/dataflows"
# Refresh dataflow
DATAFLOW_ID="<dataflow-id>"
fab api -A powerbi -X post "groups/$WS_ID/dataflows/$DATAFLOW_ID/refreshes"Gen2 (Fabric dataflows):
# Gen2 dataflows are Fabric items - use standard fab commands
fab ls "ws.Workspace" | grep DataflowGen2
fab get "ws.Workspace/Flow.DataflowGen2" -q "id"Apps
Workspace Apps (published from workspaces):
# List user's apps
fab api -A powerbi apps
# List all apps (admin)
fab api -A powerbi 'admin/apps?$top=100'
# Get app details
APP_ID="<app-id>"
fab api -A powerbi "apps/$APP_ID"
# Get app reports
fab api -A powerbi "apps/$APP_ID/reports"
# Get app dashboards
fab api -A powerbi "apps/$APP_ID/dashboards"Org Apps (template apps from AppSource):
# Org apps are installed from AppSource marketplace
# They appear in the regular apps endpoint after installation
# No separate API for org app catalog - use AppSourceAdmin Operations
Workspaces
# List all workspaces (requires $top)
fab api -A powerbi 'admin/groups?$top=100'
# Response includes: id, name, type, state, capacityId, pipelineId
# Get workspace users
fab api -A powerbi "admin/groups/$WS_ID/users"
# Native alternative (preferred):
# fab acl ls "ws.Workspace"Items
# List all items in tenant
fab api admin/items
# Response includes: id, type, name, workspaceId, capacityId, creatorPrincipalSecurity Scanning
# Reports shared with entire org (security risk)
fab api -A powerbi "admin/widelySharedArtifacts/linksSharedToWholeOrganization"
# Reports published to web (security risk)
fab api -A powerbi "admin/widelySharedArtifacts/publishedToWeb"Activity Events
# Get activity events (last 30 days max)
# Dates must be in ISO 8601 format with quotes
START="2025-11-26T00:00:00Z"
END="2025-11-27T00:00:00Z"
fab api -A powerbi "admin/activityevents?startDateTime='$START'&endDateTime='$END'"Common Patterns
Extract ID for Chaining
# Get ID and remove quotes
WS_ID=$(fab get "ws.Workspace" -q "id" | tr -d '"')
MODEL_ID=$(fab get "ws.Workspace/Model.SemanticModel" -q "id" | tr -d '"')
# Use in API call
fab api -A powerbi "groups/$WS_ID/datasets/$MODEL_ID/refreshes" -X post -i '{"type":"Full"}'Pagination
# APIs with $top often have pagination
# Check for @odata.nextLink in response
fab api -A powerbi 'admin/groups?$top=100' -q "@odata.nextLink"
# Use returned URL for next pageError Handling
# Check status_code in response
# 200 = success
# 400 = bad request (check parameters)
# 401 = unauthorized (re-authenticate)
# 403 = forbidden (insufficient permissions)
# 404 = not foundAPI Audiences
| Audience | Flag | Base URL | Use Case |
|---|---|---|---|
| Fabric | (default) | api.fabric.microsoft.com | Fabric items, workspaces, admin |
| Power BI | -A powerbi | api.powerbi.com | Reports, datasets, gateways, pipelines |
Most admin operations work with both APIs but return different formats.