Microsoft Fabric is a SaaS platform, so your notebooks, data pipelines, semantic models and reports are items in a workspace, not files on a disk. That single fact reshapes how continuous integration and deployment work. There is no git push that magically promotes a report to production; instead Fabric offers three distinct mechanisms, each solving a different part of the problem. Reach for the wrong one and you'll fight the platform; combine them well and dev-to-prod becomes boring, which is exactly what you want from a release process.
One caveat up front: Fabric CI/CD is a fast-moving target. Item-type coverage for Git and the deployment tools has been expanding steadily, and features ship monthly. Treat specifics here as the shape of the solution, and confirm the current support matrix in Microsoft's docs before you commit an architecture.
The three building blocks
Almost every Fabric CI/CD conversation is really about picking among these — and, in mature setups, layering them:
| Tool | What it is | Source of truth | Best for |
|---|---|---|---|
| Git integration | Two-way sync between a workspace and an Azure DevOps / GitHub branch | The Git repo | Version history, branching, code review |
| Deployment Pipelines | In-Fabric promotion across paired Dev / Test / Prod stages | Fabric workspaces | GUI-driven promotion, no repo required |
| fabric-cicd | Python library that publishes item definitions from a repo via REST | The Git repo | Automated, code-first release pipelines |
| Fabric REST APIs | Low-level item, Git and pipeline endpoints | Whatever you build on them | Custom automation the above can't express |
Git and Deployment Pipelines are native Fabric features. fabric-cicd is a Microsoft-published open-source library that sits on top of the REST APIs to make code-first deployment ergonomic. They are not mutually exclusive — the recommended pattern near the end of this article uses more than one.
1. Git integration — version control for the workspace
Git integration connects a workspace to a single branch of an Azure DevOps or GitHub repository. Supported items are serialised into folders of definition files, and the sync runs both ways: you commit workspace changes to the branch, or update the workspace from the branch.
- What it gives you — real version history, diffs, branching and pull-request review for Fabric content, instead of undocumented clicks in a workspace.
- Isolated development — the "branch out to a new workspace" flow gives each developer (or each feature) their own workspace bound to a feature branch, so people aren't editing the same shared canvas.
- One workspace, one branch — the mapping is 1:1. Parallel work means multiple workspaces, not multiple branches in one.
The gotchas are mostly about coverage and boundaries:
- Not every item type is supported — the list keeps growing, but anything unsupported won't serialise and has to be handled another way.
- Some settings don't round-trip — certain item properties and, deliberately, sensitive values aren't stored in Git, so connections and secrets still need per-environment wiring.
- Conflicts are resolved in Git — treat the repo as authoritative and merge there, not by hand-editing the workspace.
2. Deployment Pipelines — promotion inside Fabric
Deployment Pipelines are the native, GUI-driven way to move content up a chain of environments — typically Dev → Test → Prod — using paired workspaces. You compare two stages, see what changed, and deploy the difference. No external repo is required.
- Deployment rules & parameters — per-stage rules swap data-source connections and parameter values, so a dataset points at the dev database in Dev and the prod database in Prod.
- Fast and visual — excellent for teams that want promotion without standing up a full DevOps pipeline, and for a human-approved final hop into production.
- Automatable — the Deployment Pipelines REST APIs let you trigger and gate deployments from Azure DevOps or GitHub if you want the visual model but scripted control.
"Deployment Pipelines move content between environments. They are not version control — the history and source of truth still belong in Git."
The limitation to remember: state lives inside Fabric, parameterisation is more limited than a code-first flow, and you need the paired-workspace structure. For teams that want the repo to be authoritative and the whole release scripted, that's where fabric-cicd comes in.
3. fabric-cicd — code-first deployment from a repo
fabric-cicd is an open-source Python library from Microsoft that publishes Fabric item definitions from a source-controlled folder to a target workspace through the Fabric REST APIs. It's built for the code-first world: a GitHub Actions or Azure DevOps pipeline that, on merge, deploys the repo's items to Test and then Prod, authenticated by a service principal.
- Repo is the source of truth — you deploy what's committed, not what someone left in a workspace.
- Growing item coverage — notebooks, data pipelines, semantic models, reports, environments and more; check the docs for the current list.
- Service-principal auth — you pass an explicit
azure-identitycredential (fabric-cicd no longer falls back to implicit default auth), so nothing is embedded and the identity survives staff changes. - Environment parameterisation — a
parameter.ymlfile rewrites environment-specific values (workspace IDs, endpoints, connection strings) at publish time.
A minimal deployment script is genuinely short. Note the explicit token_credential — current versions require you to pass a credential and no longer fall back to implicit/default auth:
import os
from azure.identity import ClientSecretCredential
from fabric_cicd import (
FabricWorkspace,
publish_all_items,
unpublish_all_orphan_items,
)
# An explicit credential is now required. Use a service principal in CI...
credential = ClientSecretCredential(
tenant_id=os.environ["AZURE_TENANT_ID"],
client_id=os.environ["AZURE_CLIENT_ID"],
client_secret=os.environ["AZURE_CLIENT_SECRET"],
)
# ...or AzureCliCredential() for a local run after `az login`.
target = FabricWorkspace(
workspace_id="<target-workspace-guid>",
repository_directory="./workspace",
item_type_in_scope=["Notebook", "DataPipeline", "SemanticModel", "Report"],
environment="PROD", # selects the value set in parameter.yml
token_credential=credential, # required; pass all args as keywords
)
publish_all_items(target)
unpublish_all_orphan_items(target) # remove items deleted from the repo
The parameterisation lives beside your items. A parameter.yml maps a value found in the dev definitions to the right value per environment:
find_replace:
- find_value: "00000000-dev-workspace-guid"
replace_value:
TEST: "11111111-test-workspace-guid"
PROD: "22222222-prod-workspace-guid"
- find_value: "https://dev-endpoint.datawarehouse.fabric.microsoft.com"
replace_value:
TEST: "https://test-endpoint.datawarehouse.fabric.microsoft.com"
PROD: "https://prod-endpoint.datawarehouse.fabric.microsoft.com"
And the pipeline that runs it needs nothing exotic — check out the repo, install the library, hand it a service principal through environment variables:
name: Deploy to Fabric
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- run: pip install fabric-cicd
- name: Publish items
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
run: python deploy.py
It deploys definitions, not data. fabric-cicd publishes the notebook code, the pipeline JSON, the model and the report — never the rows in your Lakehouse or Warehouse. Data is loaded or refreshed separately per environment. Treating a deployment as a way to move data is the most common misunderstanding we see.
Variable Libraries vs parameter.yml
Microsoft now presents the Variable Library as a first-class part of the Fabric CI/CD platform, and it overlaps with what parameter.yml does — so it's worth being clear on when to reach for each.
A Variable Library is a Fabric item: a bucket of user-defined variables, each carrying a set of per-stage values (one value set for Dev, one for Test, one for Prod), with exactly one set active per stage. Other items in the workspace consume those variables at runtime — a pipeline's wait time, a notebook's default lakehouse, a copy activity's source, a shortcut's data source. Change the value once and every consumer picks it up. It's configuration-as-code, native to Fabric, versioned in Git and honoured by deployment pipelines. parameter.yml, by contrast, is a deployment-time mechanism — fabric-cicd rewrites text in the item definitions as they're published.
| Variable Library | parameter.yml (fabric-cicd) | |
|---|---|---|
| Nature | Fabric-native item, resolved at runtime | Find / replace at publish time |
| Best for | Config a Fabric item consumes — lakehouse IDs, connections, per-stage values | Values baked into definitions that must change on deploy — workspace GUIDs, endpoints, strings |
| Managed in | Fabric UI or APIs; shared across items | A YAML file in the repo, beside the items |
| Reach | Only items that support Variable Libraries | Any text in any serialised item definition |
Rule of thumb: prefer a Variable Library when the value is configuration a Fabric item natively consumes and you want it managed in-product and shared; fall back to parameter.yml for deploy-time substitution the library can't reach — most commonly workspace and connection IDs embedded in definitions, or item types the library doesn't yet support. They're complementary, not competing.
The setup we actually recommend
Microsoft's documentation frames Fabric CI/CD as several workflow options on one platform — Git integration, Deployment Pipelines, the Fabric REST APIs and CLI, Variable Libraries, and infrastructure-as-code with Terraform and fabric-cicd (which Microsoft calls the most widely adopted deployment tool). The pattern below maps mostly to the Git + fabric-cicd path, with Deployment Pipelines as an optional hybrid for a human-approved final hop into production.
For a mid-market team that wants Fabric releases to be safe and repeatable without a platform team's worth of overhead, this is the pragmatic pattern:
- The Git repo is the single source of truth. Use Fabric Git integration so authoring stays convenient, but the branch — not any workspace — is authoritative.
- Developers work in isolated workspaces. Branch out per developer or per feature, then merge to
mainthrough a reviewed pull request. - Automate promotion with
fabric-cicd. On merge tomain, a pipeline publishes to Test; a manual approval gate then publishes to Prod. Everything runs as a service principal, never a person. - Parameterise everything environment-specific. Workspace and capacity IDs, SQL endpoints, connection strings — all via
parameter.yml, so the same repo lands correctly in every stage. - Keep data and secrets out of the deployment. Ship definitions; handle data loads, refreshes and connection secrets with their own per-environment steps.
Deployment Pipelines still earn their place for teams that prefer a visual, human-approved final hop into production — you can drive them from the same CI with their REST APIs. The point isn't tool purity; it's that the repo is authoritative and no promotion happens by hand.
The gotchas that break releases
Most failed Fabric deployments trace back to one of these:
- Hardcoded IDs. A workspace, capacity or connection GUID left pointing at Dev is the number-one reason a deployment works in Dev and breaks in Prod. Parameterise all of them.
- Service-principal prerequisites. The tenant admin settings that allow service principals to call the Fabric APIs and to update workspaces and Git must be enabled, and the SPN must be an Admin or Member on the target workspace. Miss this and auth fails with unhelpful errors.
- Item coverage gaps. If an item type isn't yet supported by Git or the deployment tool, it won't move — you'll need REST or a manual step until support lands.
- Connections and gateways. Definitions deploy, but data-source bindings frequently need rebinding per environment; don't assume a report arrives already connected.
- Report-to-model binding. Reports reference semantic models by ID; across stages those references have to resolve to the right model, or you get a report pointing at the wrong (or dev) dataset.
- Assuming data comes along. It doesn't. Plan the data-seeding and refresh story as a first-class part of the release, separate from the definition deployment.
The Fabric CI/CD checklist
The short version — worth confirming before a Fabric environment carries real releases:
- Put item definitions under source control (Git integration or export); the repo is the source of truth.
- Give developers isolated workspaces per branch/feature; merge via reviewed pull requests.
- Automate deployment with
fabric-cicdand a service principal — never a personal account. - Parameterise every workspace/capacity ID, endpoint and connection via
parameter.yml. - Deploy definitions only; handle data loads, refreshes and secrets separately.
- Gate production behind an approval; log and verify each release.
Frequently asked questions
What is fabric-cicd?
fabric-cicd is an open-source Python library published by Microsoft that deploys Microsoft Fabric item definitions from a source-controlled repository to a target workspace using the Fabric REST APIs. You point it at a folder of serialised items — notebooks, data pipelines, semantic models, reports and a growing list of others — authenticate with a service principal via azure-identity, and it publishes them. It's designed for code-first, automated release pipelines in GitHub Actions or Azure DevOps, as opposed to the GUI-driven Deployment Pipelines feature inside Fabric.What's the difference between Fabric Git integration and Deployment Pipelines?
fabric-cicd or the Deployment Pipelines APIs.Does fabric-cicd deploy data or just item definitions?
fabric-cicd publishes the metadata and logic of items — the notebook code, pipeline JSON, semantic model, report — not the data inside a Lakehouse or Warehouse. Tables, files and rows are never moved by the deployment. Data has to be loaded or refreshed separately in each environment, and connections often need rebinding per stage. Treating the deployment as a way to move data is one of the most common misunderstandings.Can you use a service principal for Microsoft Fabric CI/CD?
fabric-cicd you now pass an explicit azure-identity credential — for example a ClientSecretCredential built from the AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET environment variables, or AzureCliCredential for local runs — as it no longer falls back to implicit default authentication.How do you parameterise connections and IDs across Dev, Test and Prod?
fabric-cicd this is a parameter.yml file of find-and-replace rules: a value that appears in the dev item definitions — a workspace GUID, a SQL endpoint, a connection string — is replaced with the correct value for the target environment at publish time, selected by the environment argument. In Deployment Pipelines the equivalent is deployment rules and parameter rules on each stage. Either way, hardcoded workspace, capacity and connection IDs are the single most common cause of a deployment that works in Dev and breaks in Prod, so everything environment-specific must be parameterised.Further reading
Fabric CI/CD moves quickly — these are the primary sources to track for current detail:
- Introduction to CI/CD in Microsoft Fabric — the platform overview and the workflow options.
- Git integration and Deployment pipelines — the two native workflow options, with their supported-item lists.
- Variable Library overview — Fabric-native, per-stage configuration as code.
- fabric-cicd documentation and GitHub repo — the library's current API, authentication and parameterisation reference.
Standing up Fabric the right way — source control, isolated environments, automated releases — is exactly what our migration and platform work puts in place from day one. Book a scoping call.
CI/CD in Fabric isn't about finding one magic button — it's about deciding what's authoritative (the repo), how change is reviewed (branches and pull requests), and how promotion happens (automated, parameterised, service-principal-driven). Get those three right and releases stop being events. For the architectural choices that sit alongside your pipeline, see our guide to the Fabric decisions that are hard to undo, and the phase-by-phase Fabric implementation checklist.