/fabric-notebook-deploy — Deploy a Notebook to a Fabric Workspace
You are deploying a Python notebook (Databricks-style .py or plain .ipynb) to a Microsoft Fabric workspace.
This skill is the Fabric counterpart of /databricks-deploy.
Command (start here)
The framework ships this as a runnable command — convert + create/update in one
call, no MCP needed (Fabric auth from az login or a service principal in
credentials.yaml):
python -m core.cli fabric-notebook-deploy path/to/nb.py --env dev
python -m core.cli fabric-notebook-deploy path/to/nb.py --workspace {ws_id} --name MyNotebook
python -m core.cli fabric-notebook-deploy --workspace {ws_id} --list # read-only listing
It resolves the workspace (--workspace > overlay power_bi.workspace_id >
platforms.fabric.workspace_id), converts a Databricks-style .py via the
in-repo databricks_to_ipynb parser (.ipynb passes through), confirms before
writing, then creates or updates the Fabric Notebook item and logs to ops.log.
Structural conversion only — semantic rewrites (dbutils.fs → mssparkutils.fs,
Unity Catalog → Lakehouse) belong to /migration-assess.
The steps below document what the command does end-to-end (and the building
blocks — read_and_convert, FabricConnector) for when you need to extend it
(e.g. the optional move-to-folder step the CLI does not yet cover).
Prerequisites
FabricConnectorcredentials configured incredentials.yaml(seecore/connectors/fabric.pydocstring)az login --allow-no-subscriptionsto the target tenant, or service principal incredentials.yamlunderfabric.{client_id, client_secret, tenant_id}- For
.pyDatabricks-style sources: the in-repo convertercore/parsers/databricks_to_ipynb.py(no external dependency) - Contributor access on the target workspace
Usage
/fabric-notebook-deploy {local_path} --env {env} # deploy to env's workspace
/fabric-notebook-deploy {local_path} --workspace {ws_id} # explicit workspace override
/fabric-notebook-deploy {local_path} --env {env} --folder {f} # place under a workspace folder
/fabric-notebook-deploy --list --env {env} # list notebooks in workspace
Arguments via $ARGUMENTS:
- positional
local_path—.py(Databricks-style or plain) or.ipynb --env {env}— resolves workspace fromoverlay.power_bi.workspace_idor env_config--workspace {ws_id}— override workspace--folder {name}— place under a workspace folder (Fabric requires create-in-root-then-move)--list— read-only listing of notebooks already in the workspace--name {display_name}— override display name (defaults to source filename)
Behavior
Step 1: Resolve Project & Workspace
Same pattern as /powerbi-publish:
overlay.power_bi.workspace_id # preferred
environments.{env}.platforms.fabric.workspace_id # fallback
Step 2: Read & Convert Source
read_and_convert handles both formats — .py Databricks-style sources are
converted, .ipynb files pass through unchanged:
from core.parsers.databricks_to_ipynb import read_and_convert
notebook_content = read_and_convert(local_path)
What the converter does:
# Databricks notebook sourceheader → stripped# COMMAND ----------→ cell boundary# MAGIC %md→ markdown cell# MAGIC %sql→%%sqlcode cell;%pip/%run/ etc. preserved as magics- a plain
.pywith no Databricks markers → a single code cell - an
.ipynbsource → passed through unchanged
Structural translation only — semantic rewrites (dbutils.fs → mssparkutils.fs,
Unity Catalog → Lakehouse paths) are out of scope and belong to /migration-assess.
Step 3: Check Existing
from core.connectors.fabric import FabricConnector
connector = FabricConnector.from_credentials(load_credentials(project_root))
items = connector.client.list_items(workspace_id, item_type="Notebook")
existing = next((i for i in items if i["displayName"] == display_name), None)
Step 4: Build Fabric Definition
import base64, json
parts = [
{
"path": "notebook-content.ipynb",
"payload": base64.b64encode(json.dumps(notebook_content).encode()).decode(),
"payloadType": "InlineBase64",
},
# Fabric Notebook items also require .platform metadata:
{
"path": ".platform",
"payload": base64.b64encode(json.dumps({
"$schema": ".../platformProperties/2.0.0/schema.json",
"metadata": {"type": "Notebook", "displayName": display_name},
"config": {"version": "2.0", "logicalId": "00000000-0000-0000-0000-000000000000"},
}).encode()).decode(),
"payloadType": "InlineBase64",
},
]
definition = {"format": "ipynb", "parts": parts}
Step 5: Confirmation Gate
Before pushing to cert or prod, show:
Notebook deploy — {env}:
- Source: {local_path}
- Target: {workspace_id}{/folder}
- Action: {Create | Update existing {item_id}}
Proceed? (yes/no —
prodrequires double-confirm)
Step 6: Deploy
Create:
result = connector.client.create_item(
workspace_id, display_name=display_name, item_type="Notebook", definition=definition,
)
notebook_id = result["id"]
Update:
connector.client.update_item_definition(workspace_id, existing["id"], definition)
notebook_id = existing["id"]
Poll the LRO on 202.
Step 7: Move to Folder (Optional)
Fabric API doesn't support creating items directly inside folders with a definition — must create in root, then move:
POST /v1/workspaces/{ws_id}/items/{notebook_id}/move
{"folderId": "{folder_id}"}
Resolve folder_id via GET /v1/workspaces/{ws_id}/folders (filter by displayName).
Step 8: Log
{ISO_timestamp} | {role} | FABRIC-NB-DEPLOY | {env} | {local_path} -> {workspace_id}/{folder}: {created|updated} {notebook_id} | {ok|fail}
Error Handling
| Error | Cause | Fix |
|---|---|---|
401 after az login |
Token cache stale | Force-refresh via connector.auth.get_token(force_refresh=True) |
| 403 on workspace | Lacks Contributor role | Request access from workspace admin |
| 409 LockConflict | Concurrent operation on the notebook | Wait 30s and retry |
Invalid notebook format |
Source doesn't match expected format | Use Databricks-style or .ipynb |
Folder not found |
Folder hasn't been created yet | Create via Fabric portal or extend skill to create folders |
Notes
- Fabric Notebook items support
format: ipynb(canonical) andformat: py(Spark Python). The converter currently always producesipynb. - Token cache (when using MSAL device-code) lives at
~/.ade/fabric_token_cache.jsononcemsal_cacheis ported. - For Git-integrated workspaces, deployed notebooks will also sync to the Git repo on the next sync cycle.
- Large notebooks may take longer (base64 encoding overhead). The Fabric API timeout is 100s — for very large notebooks consider splitting cells.