plugins/fabric-skills/skills/deployment-pipelines-authoring-cli/SKILL.md
Manages Fabric deployment pipelines for ALM promotion across dev, test, and prod stages, including stage creation, workspace assignment, selective forward or backward deploys, operation polling, stage role assignments, and the pipeline and workspace permissions each action requires. For Git sync use git-integration-operations-cli.
npx skillsauth add microsoft/skills-for-fabric deployment-pipelines-authoring-cliInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Telemetry — MANDATORY. Every
api.fabric.microsoft.comcall must carryx-ms-fabric-skill: deployment-pipelines-authoring-cli(az rest:--headers "x-ms-fabric-skill=deployment-pipelines-authoring-cli"), including every LRO poll,fabric_lroand retry. Snippets omit it — add it anyway.
CRITICAL NOTES
- To find the workspace details (including its ID) from workspace name: list all workspaces and, then, use JMESPath filtering
- To find the item details (including its ID) from workspace ID, item type, and item name: list all items of that type in that workspace and, then, use JMESPath filtering
- To find a deployment pipeline or stage ID: list deployment pipelines (then stages) and filter by
displayNamewith JMESPath — never guess IDs.
Automates Fabric Application Lifecycle Management (ALM) with deployment pipelines: promote Fabric content between stages (typically Development → Test → Production) using the Fabric core control-plane REST API. This skill covers pipeline/stage lifecycle, workspace assignment, and stage-to-stage deployment (a long-running operation).
Read these companion documents first — this skill assumes their patterns and does not repeat them:
| Task | Reference | Notes |
|---|---|---|
| Finding Workspaces and Items in Fabric | COMMON-CLI.md § Finding Workspaces and Items in Fabric | Mandatory — resolve workspace/item IDs by name via list + JMESPath |
| Authentication & Token Acquisition | COMMON-CORE.md § Authentication & Token Acquisition | Token audience must be https://api.fabric.microsoft.com; wrong audience = 401 |
| Authentication Recipes | COMMON-CLI.md § Authentication Recipes | az login flows and token acquisition |
| Fabric Control-Plane API via az rest | COMMON-CLI.md § Fabric Control-Plane API via az rest | Always pass --resource https://api.fabric.microsoft.com |
| Core Control-Plane REST APIs | COMMON-CORE.md § Core Control-Plane REST APIs | Pagination, LRO polling, rate-limiting patterns |
| Long-Running Operations (LRO) | COMMON-CLI.md § Long-Running Operations (LRO) Pattern | Deploy is an LRO — poll /v1/operations/{id} until terminal |
| Environment URLs | COMMON-CORE.md § Environment URLs | Sovereign/gov clouds use different hosts |
| Supported item types | references/supported-item-types.md | Living list of item types a deploy can copy (per category, with preview flags) — reconcile from the official Microsoft docs (source of truth); don't guess |
| Diff two item definitions (token-efficient) | references/scripts/diff_item_definitions.py | Local tool that decodes both getDefinition payloads, normalizes auto-rebound fields, and prints only the diffs — feed the diff (not the full definitions) to the model. python references/scripts/diff_item_definitions.py source.json target.json (exit 0=same, 1=changed) |
This skill adds: how to drive the deployment-pipelines REST surface from an agentic terminal.
order starts at 0). Each stage may have at
most one assigned workspace, and a workspace can be assigned to at most one stage.sourceItemId / targetItemId fields returned by List stage items. See the Fix a broken item pairing
workflow below for the only supported repair.202 Accepted with an
operation ID; you poll for completion.List stage items returns item identity + pairing
(itemId, itemDisplayName, itemType, sourceItemId, targetItemId, lastDeploymentTime) — but no
change status, and lastDeploymentTime is the last deployment time, not the last edit time, so
it is not a reliable change signal. To deploy only changed items you must diff the two stages yourself
and build the items list (see the Deploy only changed items workflow below). The portal's "Compare"
view is server-side and not exposed via API.https://api.fabric.microsoft.com/v1/deploymentPipelines and acquire a token for the https://api.fabric.microsoft.com audience.202, capture the operation ID and poll /v1/operations/{operationId} until the state is Succeeded/Failed, then surface the result.createdWorkspaceDetails (name, and capacityId when needed) in the deploy body, or the deploy fails.401/403 rather than retrying blindly.items array ({ sourceItemId, itemType }) when the user names specific items; omit items to deploy all supported items.note on every deploy — but know it is write-only: the API accepts it and never
returns it (it appears only in the portal UI). For a programmatic audit trail, also record the deploy
externally (CI/CD logs or a Git commit message).List Deployment Pipeline Stage Items to preview what will move before deploying.api.fabric.microsoft.com in sovereign clouds — resolve the host from environment config.getDefinition output to detect changes without normalizing. Deployment auto-rebinds
embedded references in the target (pipeline notebookId/workspaceId, report→model id, Direct Lake
server/db), so a paired target's definition legitimately differs from the source even when nothing was
edited — naive hashing reports false "changed". Strip/normalize those binding fields before comparing.getDefinition calls and >100 KB — do the compare in a script
(references/scripts/diff_item_definitions.py) and surface only
the resulting change list, and for a changed item forward only the emitted diff, never the two full
definitions, to the model.Base: https://api.fabric.microsoft.com/v1. Delegated scopes are per operation — provision a service
principal with least privilege:
| Operation | Required delegated scope |
|---|---|
| List / Get (pipelines, stages, stage items, operations) | Pipeline.Read.All or Pipeline.ReadWrite.All |
| Create / Update / Delete pipeline, Update stage | Pipeline.ReadWrite.All |
| Assign / Unassign workspace | Pipeline.ReadWrite.All and Workspace.ReadWrite.All |
| Deploy stage content | Pipeline.Deploy |
Deploy uses its own Pipeline.Deploy scope — an app scoped only to Pipeline.ReadWrite.All gets a 403
on POST .../deploy.
| Operation | Method + Path |
|---|---|
| List pipelines | GET /deploymentPipelines |
| Create pipeline | POST /deploymentPipelines |
| Get / Update / Delete pipeline | GET|PATCH|DELETE /deploymentPipelines/{id} |
| List / Get stages | GET /deploymentPipelines/{id}/stages[/{stageId}] |
| Update stage | PATCH /deploymentPipelines/{id}/stages/{stageId} |
| List stage items | GET /deploymentPipelines/{id}/stages/{stageId}/items |
| Assign workspace to stage | POST /deploymentPipelines/{id}/stages/{stageId}/assignWorkspace |
| Unassign workspace from stage | POST /deploymentPipelines/{id}/stages/{stageId}/unassignWorkspace |
| Deploy stage content (LRO) | POST /deploymentPipelines/{id}/deploy |
| List operations (≤20 recent) | GET /deploymentPipelines/{id}/operations |
| Get operation (with execution plan) | GET /deploymentPipelines/{id}/operations/{operationId} |
| Role assignments | GET|POST|DELETE /deploymentPipelines/{id}/roleAssignments[/{principalId}] |
{ "displayName", "description"?, "stages": [ { "displayName", "description"?, "isPublic" } ] } — 2–10 stages.{ "workspaceId" }.{ "sourceStageId", "targetStageId", "items"?: [ { "sourceItemId", "itemType" } ], "note"?, "options"?: { "allowCrossRegionDeployment": false }, "createdWorkspaceDetails"?: { "name", "capacityId"? } }.Deployment pipeline operations are governed by two independent permission systems: your role on the
pipeline (its own roleAssignments) and your role on each workspace involved. You generally need
both. Deployment pipelines require a Fabric capacity/subscription, and the pipeline creator becomes the
pipeline Admin.
| Operation | Pipeline role | Workspace role(s) |
|---|---|---|
| Create a pipeline | — (becomes Admin on create) | Admin of a Fabric workspace (feature access prerequisite) |
| Get / list pipeline, stages, items, operations | Admin (shared access) | — |
| Update / delete pipeline; manage roleAssignments | Admin | — |
| Assign / unassign a workspace to a stage | Admin | Admin of the workspace being (un)assigned |
| Deploy stage content | Admin | at least Contributor/Member on both the source and target stage workspaces (higher role if the target items require it) |
| Deploy to an empty target stage (creates a workspace) | Admin | capacity-assignment permission to place the new workspace on a capacity; the deploying user becomes the only Admin of the newly created workspace and owner of cloned semantic models |
Notes:
403, distinguish missing pipeline role from missing workspace role in the message so the user knows which access to request.Create a pipeline → POST the display name + ordered stages; capture the returned stage IDs (they are what you deploy between).
Assign a workspace to a stage → resolve stage ID and workspace ID by name, verify both are unassigned,
then POST assignWorkspace. Requires admin on both the pipeline and the workspace.
Deploy content → resolve source/target stage IDs, optionally preview stage items, POST deploy (all
items or a selected items list) with a note, then poll the returned operation to completion and report
which items moved.
Deploy only changed items → there is no diff/compare API, so compute the delta yourself. List stage items gives identity + pairing only (no timestamps, no change status), so change detection means comparing
item definitions:
GET .../stages/{stageId}/items) — each source row carries sourceItemId
(as itemId) and its paired targetItemId (absent = new/unpaired). Also list the target stage to find
deleted items (present in target, not paired from any source). Pair by targetItemId; fall back to
(itemType, displayName) only when no pipeline pairing exists (name-matching can't detect renames).SQLEndpoint is auto-created per Lakehouse/Warehouse and should
not be diffed or deployed on its own; it follows its parent.targetItemId) → deploy.POST .../items/{id}/getDefinition on both stages, then diff. Run
references/scripts/diff_item_definitions.py on the two
responses: it decodes each part, normalizes the auto-rebound fields (pipeline notebookId/
workspaceId, report model id, Direct Lake server/db) for you, and prints only the differing parts
(exit 1 = changed, 0 = identical) — so you don't hand-roll hashing or false-positive on unedited
items. Note the getDefinition contract differs by type:
Notebook/SemanticModel/Report are LRO (202 → poll → GET .../result);
DataPipeline returns 200 synchronously.getDefinition diff: Warehouse has no item-definition API, and
Lakehouse / Environment do expose getDefinition but their definitions capture structure/metadata
only (not table data or most content edits) — so for all three, diff by presence and treat as changed
only when new or when the user indicates content changed.getDefinition calls; they dominate
latency) and surface only the computed change list. To let the model reason about what changed in an
item, forward only the emitted diff (diff_item_definitions.py output), not the two full definitions —
a normalized diff is typically a few lines versus the >100 KB of raw payloads.deploy with the { sourceItemId, itemType } array and a note, and poll to completion.Deletions do not propagate via a selective
itemsdeploy — an item removed from the source stage is not removed from the target by a selective deploy. Use a full deploy (omititems) to propagate deletions, or delete the target item manually, and warn the user of this before promoting.
Fix a broken item pairing → pairing (the source↔target connection Fabric maintains, a.k.a. autobinding) is not settable via any REST API. When two items that should be paired across stages are not — surfaced by a deploy that duplicates an item instead of overwriting it, an autobind/dependency failure, or the user reporting it — the only supported repair is to unassign the affected stage's workspace and re-assign it, which forces Fabric to rebuild the pairing on the next deploy. This is destructive:
POST .../stages/{stageId}/unassignWorkspace, then
POST .../stages/{stageId}/assignWorkspace with the same workspaceId, and re-run the deploy so the
pairing is re-established.Track / audit → List operations for recent history; Get operation to inspect the execution plan and
per-item status of a specific deployment.
WorkspaceMigrationOperationInProgress (HTTP 400). Always poll the current
operation to a terminal state before starting the next stage promotion (this is also why the assign
workspace call fails during an active deployment).Alm_InvalidRequest_WorkloadUnavailable. Newly
assigned workspaces need ~60–120 s for workload services (Lakehouse, Notebook, etc.) to initialize.
Mitigate by waiting before the first deploy, or deploy Power BI items (SemanticModel, Report) first and
Fabric-native items (Lakehouse, Notebook) after a short delay; retry on this error. Subsequent deploys
are reliable once workloads are warm.x-ms-operation-id response header (alongside Location), not the
202 body. az rest does not surface response headers cleanly — for reliable header capture in automation,
issue the deploy with curl -i (or Python requests) and parse the header, then poll
/v1/operations/{operationId} with az rest.Deployment-pipeline displayName is unique tenant-wide — a create for a name
already in use fails with Alm_InvalidRequest_DuplicateAlmPipelineName (HTTP 400,
"pipeline name ... is already in use"). To make a create re-runnable, drop any
existing pipeline of that name first, then create. Write the JSON body to a file
and pass it with --body @file.json — an inline multi-line --body '{...}' is
mangled into an empty body on Windows/PowerShell (az is az.cmd), which the API
rejects with InvalidInput: Unexpected end when reading JSON.
# Idempotent: delete an existing "Sales Analytics ALM" if present, then create.
EXISTING=$(az rest --method GET --resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines" \
--query "value[?displayName=='Sales Analytics ALM'].id | [0]" --output tsv)
if [ -n "$EXISTING" ]; then
az rest --method DELETE --resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines/$EXISTING"
fi
cat > /tmp/create-pipeline.json << 'EOF'
{
"displayName": "Sales Analytics ALM",
"description": "Dev/Test/Prod promotion for Sales Analytics",
"stages": [
{ "displayName": "Development", "isPublic": false },
{ "displayName": "Test", "isPublic": false },
{ "displayName": "Production", "isPublic": true }
]
}
EOF
az rest --method POST \
--resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines" \
--headers "Content-Type=application/json" \
--body @/tmp/create-pipeline.json
# $PIPELINE_ID and $DEV_STAGE_ID resolved via list + JMESPath; $WS_ID resolved from workspace name.
az rest --method POST \
--resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines/$PIPELINE_ID/stages/$DEV_STAGE_ID/assignWorkspace" \
--headers "Content-Type=application/json" \
--body "{ \"workspaceId\": \"$WS_ID\" }"
# Kick off the deployment (returns 202 + an operation id header).
az rest --method POST \
--resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines/$PIPELINE_ID/deploy" \
--headers "Content-Type=application/json" \
--body "{ \"sourceStageId\": \"$DEV_STAGE_ID\", \"targetStageId\": \"$TEST_STAGE_ID\", \"note\": \"Promote validated dev build\" }" \
--verbose
# Then poll /v1/operations/{operationId} until state is Succeeded or Failed
# (see COMMON-CLI § Long-Running Operations pattern).
az rest --method POST \
--resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines/$PIPELINE_ID/deploy" \
--headers "Content-Type=application/json" \
--body '{
"sourceStageId": "'"$TEST_STAGE_ID"'",
"targetStageId": "'"$PROD_STAGE_ID"'",
"items": [
{ "sourceItemId": "'"$MODEL_ID"'", "itemType": "SemanticModel" },
{ "sourceItemId": "'"$REPORT_ID"'", "itemType": "Report" }
],
"note": "Promote reviewed model + report to production"
}'
# 1. List paired items for BOTH stages. Each source row carries itemId (= sourceItemId)
# and its paired targetItemId (absent = new/unpaired). There is NO timestamp or
# change-status field, so "changed" must be detected from item definitions.
az rest --method GET --resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines/$PIPELINE_ID/stages/$SOURCE_STAGE_ID/items" \
> source-items.json
# 2. NEW items = unpaired (no targetItemId). Exclude system-managed SQLEndpoint children.
NEW=$(jq '[.value[]
| select(.itemType != "SQLEndpoint")
| select(has("targetItemId") | not)
| { sourceItemId: .itemId, itemType: .itemType }]' source-items.json)
# 3. CHANGED items (paired): fetch getDefinition from BOTH stages, then diff with
# references/scripts/diff_item_definitions.py. It decodes each part, NORMALIZES the
# auto-rebound fields (notebookId/workspaceId in pipelines, model id in reports,
# server/db in Direct Lake models) and prints ONLY the differing parts. Notebook/
# SemanticModel/Report are LRO (202 -> poll -> /result); DataPipeline returns 200 sync.
# Parallelize the independent getDefinition calls. Per paired definition-backed item:
# getDefinition SOURCE_WS id > src.json
# getDefinition TARGET_WS targetItemId > tgt.json
# python references/scripts/diff_item_definitions.py src.json tgt.json > diff.json
# # exit 1 = changed -> add { sourceItemId, itemType } to $ITEMS; exit 0 = unchanged.
# # Forward diff.json (NOT src.json/tgt.json) to the model to explain the change.
# Merge NEW + CHANGED into $ITEMS. When equality can't be proven, include the item.
# 4. Selective deploy of just the changed set.
az rest --method POST --resource https://api.fabric.microsoft.com \
--url "https://api.fabric.microsoft.com/v1/deploymentPipelines/$PIPELINE_ID/deploy" \
--headers "Content-Type=application/json" \
--body "$(jq -n --arg s "$SOURCE_STAGE_ID" --arg t "$TARGET_STAGE_ID" --argjson items "$ITEMS" \
'{ sourceStageId: $s, targetStageId: $t, items: $items, note: "Deploy only changed items" }')"
# Then poll /v1/operations/{operationId} until terminal.
# NOTE: a selective deploy does NOT propagate deletions — use a full deploy (omit items) for those.
User: "Promote my Sales dev workspace to the Test stage and tell me when it's done."
Assistant (behavior):
sourceStageId (Development) and targetStageId (Test).deploy with a note, receives 202, extracts the operation ID./v1/operations/{operationId} until terminal, then reports success and the list of deployed items (or the failure reason).tools
Manages Fabric Spark work, including notebook cell code with %%configure, %%sql, PySpark and notebookutils, named notebook runs, Livy sessions, triage of failed or OOM notebook and pipeline Spark runs, and the Materialized Lake View (MLV) lifecycle. Load it before writing MLV SQL, since CREATE MATERIALIZED LAKE VIEW and its CONSTRAINT clause are Fabric-only. KQL materialized views belong to eventhouse-cli.
tools
Governs Microsoft Fabric OneLake catalog health, protection, and trust through Fabric Admin, Core, and Power BI REST APIs. Use for tenant or owner-scoped audits and guarded remediation of domains, workspace assignment, capacity, labels, tags, descriptions, refresh, and item identity. Catalog item discovery belongs to search-consumption-cli.
tools
Runs the Fabric Git integration lifecycle through fab api or az rest, including connecting a workspace to Azure DevOps or GitHub, committing, updating from Git, reading sync status, resolving conflicts, disconnecting a connected workspace, and automating sync with a service principal. For stage promotion use deployment-pipelines-authoring-cli. Branch switching, fab deploy, fabric-cicd and cross-workspace rebinding are out of scope.
tools
Manages Fabric IQ Ontology items, including entity and relationship types, data bindings, and definition updates, plus schema, lineage, grounding, and graph-walk exploration. Use for ontology modelling and traversal. For natural-language questions over a Power BI report use fabriciq.