Labsco
microsoft logo

dataflows-authoring-cli

✓ Official716

by microsoft · part of microsoft/skills-for-fabric

Update Check — ONCE PER SESSION (mandatory) The first time this skill is used in a session, run the check-updates skill before proceeding.

🔥🔥🔥FreeQuick setup
🧩 One of 7 skills in the microsoft/skills-for-fabric package — works on its own, and pairs well with its siblings.

Update Check — ONCE PER SESSION (mandatory) The first time this skill is used in a session, run the check-updates skill before proceeding.

Inspect the full instructions your agent will receiveExpand

This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.

by microsoft

Update Check — ONCE PER SESSION (mandatory) The first time this skill is used in a session, run the check-updates skill before proceeding. npx skills add https://github.com/microsoft/skills-for-fabric --skill dataflows-authoring-cli Download ZIPGitHub716

Update Check — ONCE PER SESSION (mandatory) The first time this skill is used in a session, run the check-updates skill before proceeding.

  • GitHub Copilot CLI / VS Code: invoke the check-updates skill.

  • Claude Code / Cowork / Cursor / Windsurf / Codex: compare local vs remote package.json version.

  • Skip if the check was already performed earlier in this session.

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

dataflows-authoring-cli — Dataflows Gen2 Authoring via CLI

Table of Contents

This skill (SKILL.md)

Section Notes Tool Stack az + jq + base64 + curl Connection Workspace/dataflow ID discovery Agentic Workflows Start here. A: create end-to-end; B: modify existing; C: preview loop MUST DO / AVOID / PREFER Authoring rules Troubleshooting Symptom → fix table Examples Runnable bash + PowerShell recipes Output Expectations Response conventions

References (in references/)

File When to read authoring-cli-quickref.md One-liner recipes, status enums, base64 helpers, connection-binding quick patterns authoring-script-templates.md Full bash + PowerShell templates; end-to-end smoke test; LRO polling pattern connection-management.md List/create/inspect connections; supportedConnectionTypes; resolve ClusterId; ID format cheat sheet connectors.md M-side source connectors: live-verified function inventory, Lakehouse deep navigation, runtime-disabled functions (Web.Page, Web.BrowserContents), Html.Table / Csv.Document / Json.Document patterns m-language.md M language semantics for Dataflow Gen2: try record shapes, per-cell error wrapping in column transforms, each scoping in row vs sub-table contexts, optional field access [?] / Record.FieldOrDefault, quoted identifiers, sandbox-disabled symbols (File.Contents) mashup-preview.md executeQuery contract: bootstrap branch, auto-wrap rule, hard avoid for unbounded preview output-destinations.md Output destination patterns: Lakehouse Table, Lakehouse Files, Warehouse, ADX, Azure SQL. DataDestinations annotation, hidden query, loadEnabled rules, connection limitations

Common refs (in ../../common/)

File When to read COMMON-CLI.md az login, token acquisition, az rest, pagination, LRO polling, CLI gotchas. § Finding Workspaces and Items in Fabric is mandatory. COMMON-CORE.md Fabric topology, environment URLs, authentication, core REST API surface ITEM-DEFINITIONS-CORE.md Definition envelope; per-item-type payload contracts DATAFLOWS-AUTHORING-CORE.md Authoring capability matrix; 3-part definition structure; M structure; connection model; ALM / Git integration

Sister skills

Skill Use for dataflows-consumption-cli Execute persisted queries; ad-hoc read-only customMashupDocument with no intent to persist; Arrow → CSV/pandas conversion; refresh status/history.

Tool Stack

Tool Role Install az CLI Primary: Auth (az login), REST API calls (az rest), token acquisition. Pre-installed in most dev environments jq Parse and manipulate JSON responses and definition payloads. Pre-installed or trivial base64 Encode/decode definition parts for the REST API. Built into bash / [Convert]::ToBase64String() in PowerShell curl Alternative to az rest when raw HTTP control is needed. Pre-installed uuidgen Generate per-query / per-platform GUIDs for queryId and logicalId when building a new dataflow definition (Workflow A). Pre-installed on Linux/macOS; on Windows use PowerShell [guid]::NewGuid().Guid or run via WSL

Agent check — verify az, jq, and curl are available before first operation. uuidgen is only needed for Workflow A (Create). For installation and auth setup see COMMON-CLI.md.

Connection

Discover Workspace and Dataflow IDs

Per COMMON-CLI.md Finding Workspaces and Items in Fabric:

Copy & paste — that's it
# List workspaces — find workspace ID by name
az rest --method get \
 --resource "https://api.fabric.microsoft.com" \
 --url "https://api.fabric.microsoft.com/v1/workspaces" \
 --query "value[?displayName=='MyWorkspace'].id" --output tsv

# List dataflows in workspace — find dataflow ID by name
WS_ID=" "
az rest --method get \
 --resource "https://api.fabric.microsoft.com" \
 --url "https://api.fabric.microsoft.com/v1/workspaces/$WS_ID/dataflows" \
 --query "value[?displayName=='MyDataflow'].id" --output tsv

Reusable Connection Variables

Copy & paste — that's it
WS_ID=" "
DF_ID=" "
API="https://api.fabric.microsoft.com/v1"
RESOURCE="https://api.fabric.microsoft.com"

Agentic Workflows

Three workflows cover the typical authoring tasks:

  • ** A. Create a New Dataflow End-to-End ** — discover/create a connection, create the dataflow, save M + bindings, validate, optionally refresh.

  • ** B. Modify an Existing Dataflow ** — read-modify-write the definition; the canonical Discover → Formulate → Execute → Verify loop.

  • ** C. Preview-Driven Authoring Loop ** — iterate on candidate M via executeQuery before persisting via updateDefinition.

  • ** D. Output Destination ** — write query results to Lakehouse (table/files), Warehouse, ADX, or Azure SQL via DataDestinations annotation. Full reference: output-destinations.md.

A. Create a New Dataflow End-to-End

Use this when the dataflow does not yet exist. Covers the full happy path: discover-or-create a connection, create the dataflow shell, save M + bindings in one updateDefinition, validate, optionally refresh.

Steps:

  • List existing connections and filter by connectionDetails.type and the target URL/host — reuse if a match exists (GET /v1/connections + JMESPath).

  • If no match, create the connection. First GET /v1/connections/supportedConnectionTypes to discover required parameters and supported credential types, then POST /v1/connections (sync 201). Body shape and credential schemas: connection-management.md.

  • Resolve ClusterId for the composite binding. GET https://api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources with --query "value[?id=='$CONN_ID'] | [0].clusterId", audience --resource "https://analysis.windows.net/powerbi/api" (no trailing slash). The per-id route returns PowerBIEntityNotFound for cloud connections. Newly-created connections may take a few seconds to surface — retry on empty. Detail: connection-management.md § Resolving ClusterId.

  • Create the dataflow shell. POST /v1/workspaces/{ws}/dataflows with {"displayName":"…"} returns sync 201. The definition field is optional at create time and can be set in the next step.

  • Save M + connection bindings in one call. POST /v1/workspaces/{ws}/dataflows/{df}/updateDefinition?updateMetadata=true with three parts: mashup.pq (real Web.Contents / Sql.Database / …), queryMetadata.json (with connections[] populated; each connectionId is the stringified composite {"ClusterId":"…","DatasourceId":"…"}), and .platform. Typically returns sync 200; may return 202 + LRO Location on large bodies — handle both.

  • Verify the binding persisted. Re-call getDefinition, decode queryMetadata.json, and confirm connections[] is intact. Do not use GET /items/{id}/connections for verification — that endpoint reflects refresh-materialized state, not the persisted definition, and returns 0 even after a successful bind. See AVOID .

  • (Encouraged) Offer to preview output as ASCII charts. Ask the user: "Would you like me to preview the data as charts before the first refresh?" . In this create flow the definition is already saved in step 5, so the chart preview here is a post-save validation gate before you materialize via refresh — not a pre-save step. (If instead you want to validate candidate M before the first updateDefinition — e.g. iterating on the M, or bootstrap-binding a credentialed source so executeQuery can see it — use the pre-persist Preview-Driven Authoring Loop ; the chart rendering is identical, only the ordering relative to the save differs.) If accepted, call executeQuery for each entity, parse the Arrow IPC stream, render line charts (time-series) or horizontal bar charts (categories) via references/charts/line_chart.py / references/charts/bar_chart.py, and ask the user to confirm before proceeding. Details: mashup-preview.md § ASCII chart preview. If declined, proceed directly to step 8.

  • (Optional) Trigger refresh to materialize. POST .../jobs/instances?jobType=Refresh with body {"executionData":{"executeOption":"ApplyChangesIfNeeded"}}. ApplyChangesIfNeeded is required on the first refresh after any definition change — without it, Fabric refreshes the previously-applied definition. Poll the LRO until status is Completed (refresh enum) or Failed/Cancelled.

Copy & paste — that's it
# Concise skeleton — full runnable bash is Example 1 below.
# PowerShell + LRO-polled variants: references/authoring-script-templates.md

WS_ID=" "; URL=" "
RES="https://api.fabric.microsoft.com"; API="$RES/v1"
PBI="https://analysis.windows.net/powerbi/api"

# 1. List existing & try reuse
CONN_ID=$(az rest --method get --resource "$RES" --url "$API/connections" \
 --query "value[?connectionDetails.type=='Web' && connectionDetails.path=='$URL'] | [0].id" -o tsv)

# 2. Create connection if missing — see connection-management.md for full body
# 3. List+filter for ClusterId
CLUSTER_ID=$(az rest --method get --resource "$PBI" \
 --url "https://api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources" \
 --query "value[?id=='$CONN_ID'] | [0].clusterId" -o tsv)

# 4. Empty dataflow shell — sync 201
SHELL_BODY=$(mktemp --suffix=.json 2>/dev/null || mktemp)
printf '{"displayName":"my-df"}' > "$SHELL_BODY"
DF_ID=$(az rest --method post --resource "$RES" \
 --url "$API/workspaces/$WS_ID/dataflows" \
 --headers "Content-Type=application/json" \
 --body "@$SHELL_BODY" --query id -o tsv)
rm -f "$SHELL_BODY"

# 5. One-shot updateDefinition with real M + connections[] (sync 200 typical)
# Body assembly (mashup.pq + queryMetadata.json + .platform, base64-encoded;
# queryMetadata.json.connections[].connectionId = composite ClusterId/DatasourceId):
# see Example 1 below.

# 6. Verify via getDefinition (NOT GET /items/{id}/connections — see AVOID)
# 7. (optional) executeQuery — Workflow C
# 8. (optional) Refresh with executeOption=ApplyChangesIfNeeded — Example 2

One-shot vs two-step bind+save. Steps 4-5 can be one call (default; saves an HTTP round trip) or split into a bootstrap-bind updateDefinition followed by a full-M updateDefinition. Both work — see PREFER .

B. Modify an Existing Dataflow

Use this when the dataflow already exists. Canonical Discover → Formulate → Execute → Verify loop. If the dataflow does not yet exist, see Workflow A instead.

  • Discover — list workspaces, list dataflows, getDefinition (decode mashup.pq and queryMetadata.json). Validate all connections[] entries via GET /v1/connections/{id}.

  • Formulate — modify M, re-encode parts, ensure every referenced connectionId exists in the caller's connection store.

  • ExecutePOST .../updateDefinition?updateMetadata=true with all 3 parts (full replacement). Optionally trigger refresh.

  • Verify — re-call getDefinition to confirm changes; poll refresh LRO; for refresh failures, isolate M+source via executeQuery before re-triggering.

Copy & paste — that's it
# Concise skeleton — full templates: references/authoring-script-templates.md
# Acquire $TOKEN per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = $RESOURCE).
RESOURCE="https://api.fabric.microsoft.com"; API="$RESOURCE/v1"

# 1. Discover — getDefinition (handles 200 sync and 202 + LRO via curl)
HDR=$(mktemp); BODY=$(mktemp)
CODE=$(curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Length: 0" \
 "$API/workspaces/$WS_ID/dataflows/$DF_ID/getDefinition" \
 -D "$HDR" -o "$BODY" -w "%{http_code}")
if [ "$CODE" = "202" ]; then
 LOC=$(tr -d '\r' &2; exit 1 ;;
 esac
 done
else
 RESULT=$(cat "$BODY")
fi
rm -f "$HDR" "$BODY"

# Validate bound connections (connectionId is a composite JSON string — iterate safely)
QUERY_META=$(echo "$RESULT" | jq -r '.definition.parts[] | select(.path=="queryMetadata.json") | .payload' | base64 -d)
echo "$QUERY_META" | jq -c '.connections[]?' | while IFS= read -r conn; do
 RAW=$(echo "$conn" | jq -r '.connectionId')
 DATASOURCE_ID=$(echo "$RAW" | jq -r '.DatasourceId? // empty' 2>/dev/null)
 [ -z "$DATASOURCE_ID" ] && DATASOURCE_ID="$RAW"
 # GET /v1/connections/$DATASOURCE_ID to confirm access
done

# 2-3. Formulate & Execute — see Example 3
# 4. Verify — trigger refresh via curl (az rest cannot capture Location header).
# Full LRO polling: references/authoring-script-templates.md.

C. Preview-Driven Authoring Loop (pre-save executeQuery — see mashup-preview.md)

When the change touches Power Query M (new query, edited mashup, new source, changed parameters), preview the candidate customMashupDocument against the dataflow's bound connections before persisting. Catches syntax, schema, and credential errors at authoring time. Full ordered steps, bootstrap branch, auto-wrap rule, hard-avoid for unbounded preview, ASCII chart preview, and Apache Arrow handling: mashup-preview.md § Preview-Driven Authoring Loop.

Intent split. This workflow is for the pre-save intent. To execute a saved query (QueryName only) or run an ad-hoc read-only customMashupDocument with no intent to persist, use dataflows-consumption-cli. mashup-preview.md is the shared API reference for both intents.

Skip the preview only for metadata-only edits (display name, schedule, loadEnabled toggle) or when the agent records an explicit skip reason (bootstrap, prohibitive cost, side-effecting source).

D. Output Destination

Use this when the dataflow should write query results to an external store (Lakehouse table, Lakehouse files, Warehouse, ADX, Azure SQL). Extends Workflow A with DataDestinations annotations and a hidden destination query. Full reference with complete examples: output-destinations.md.

Key requirements:

  • Source query carries a [DataDestinations = {[...]}] annotation referencing the destination query by name.

  • Hidden destination query (suffixed _DataDestination) navigates to the target storage using null-safe ?[Data]? (tables) or ?[Content]? (files) operators.

  • queryMetadata.json must set "loadEnabled": false on the destination query — refresh fails without it. State this in your summary using the literal part name (e.g., "set loadEnabled: false on the destination query in queryMetadata.json").

  • Always use IsNewTarget = true for API-created dataflows, even for existing tables.

  • Bind the appropriate connection (Lakehouse: kind "Lakehouse"; Warehouse: kind "Warehouse"; ADX: kind "AzureDataExplorer"; Azure SQL: kind "Sql") with composite ClusterId/DatasourceId ID.

  • First refresh must use ApplyChangesIfNeeded to publish the draft and reconcile annotations.

  • All source columns must be typedAny-type columns are rejected by all destination types.

  • Name the definition parts in your written summary. Because the CLI transcript truncates long command bodies, the final summary (prose, not just shell commands) MUST name the three definition parts by their literal paths — mashup.pq, queryMetadata.json, and .platform — so the part names survive in the answer (e.g., "Saved mashup.pq + queryMetadata.json + .platform via updateDefinition"). Do not abbreviate queryMetadata.json to "query metadata" or the inner field queriesMetadata.

Supported destinations:

Destination Connection Kind Destination Query Function Notes Lakehouse Table Lakehouse Lakehouse.Contents(...) Path: "Lakehouse" Lakehouse Files Lakehouse Lakehouse.Contents(...) TypeSettings = [Kind = "File"], ?[Content]? Warehouse Warehouse Fabric.Warehouse(...) Path: "Warehouse", Schema/Item navigation Azure Data Explorer AzureDataExplorer AzureDataExplorer.Contents(...) Path must match connection exactly (trailing slash!) Azure SQL Sql Sql.Database(...) Path: "server;database"

Minimal steps: Create dataflow → Find/create connection → Resolve ClusterId → Save definition with OD annotations → Verify → Refresh.

Copy & paste — that's it
# Skeleton — full PowerShell recipe: references/output-destinations.md § Complete Example
WS_ID=" "; LH_ID=" "; RES="https://api.fabric.microsoft.com"

# M pattern (two queries):
# 1. Source with [DataDestinations] annotation
# 2. Hidden _DataDestination query with ?[Data]? null-safe navigation
# queryMetadata: source loadEnabled=true, destination loadEnabled=false + isHidden=true
# Refresh: {"executionData":{"executeOption":"ApplyChangesIfNeeded"}}

Examples

Platform note — examples below are bash. On Windows / PowerShell the bash patterns (MASHUP='...' heredoc, echo -n | base64 -w0, tr -d '\r' | grep -i location | awk) cause real escaping pain and refresh-pattern flakes. PowerShell variants are linked from the two highest-friction examples (Create and Refresh) below. For full PowerShell templates (Create, Refresh, Validate Connections, Bind Connection, Create Cloud Connection): authoring-script-templates.md § PowerShell. On PowerShell, prefer --body "@$env:TEMP\body.json" and write the body via [IO.File]::WriteAllText($path, $body, [System.Text.UTF8Encoding]::new($false)) over Out-File (which writes a UTF-8 BOM on Windows PowerShell 5.1 and breaks az.exe body parsing) and over inline --body "{...}" (which cmd.exe mangles).

Example 1: Create a Dataflow Gen2 from Scratch

Prompt: "Create a new Dataflow Gen2 that reads a public CSV via the Web connector, and verify it."

Agent response — runnable bash implementation of Workflow A . PowerShell variant: authoring-script-templates.md § End-to-End Smoke Test.

Copy & paste — that's it
# Prereqs: az login, jq, base64, uuidgen. Workspace must support Dataflow Gen2.
WS_ID=" "
DF_NAME="my-titanic-df"
CONN_NAME="my-titanic-web-conn"
URL="https://raw.githubusercontent.com/datasciencedojo/datasets/master/titanic.csv"
RES="https://api.fabric.microsoft.com"; API="$RES/v1"
PBI="https://analysis.windows.net/powerbi/api" # NO trailing slash

# Step 1: List existing connections, try to reuse by name.
CONN_ID=$(az rest --method get --resource "$RES" --url "$API/connections" \
 --query "value[?displayName=='$CONN_NAME'] | [0].id" -o tsv)

# Step 2: Create if missing (Web + Anonymous; see connection-management.md for other shapes).
if [ -z "$CONN_ID" ] || [ "$CONN_ID" = "null" ]; then
 BODY_FILE=$(mktemp --suffix=.json 2>/dev/null || mktemp) # GNU + BSD/macOS compatible
 cat > "$BODY_FILE" /dev/null)
 [ -n "$CLUSTER_ID" ] && [ "$CLUSTER_ID" != "null" ] && break
 sleep $((i*3))
done
# Fail-fast: an empty ClusterId silently corrupts the composite connectionId and the
# resulting updateDefinition / refresh failures are hard to debug. Stop here instead.
if [ -z "$CLUSTER_ID" ] || [ "$CLUSTER_ID" = "null" ]; then
 echo "FAIL: ClusterId not resolved for $CONN_ID after retries. Verify the connection is visible at PBI v2 (api.powerbi.com/v2.0/myorg/me/gatewayClusterDatasources)." >&2
 exit 1
fi

# Step 4: Create empty dataflow shell (sync 201).
SHELL_BODY=$(mktemp --suffix=.json 2>/dev/null || mktemp)
printf '{"displayName":"%s"}' "$DF_NAME" > "$SHELL_BODY"
DF_ID=$(az rest --method post --resource "$RES" \
 --url "$API/workspaces/$WS_ID/dataflows" \
 --headers "Content-Type=application/json" \
 --body "@$SHELL_BODY" --query id -o tsv)
rm -f "$SHELL_BODY"

# Step 5: One-shot updateDefinition — real M + composite-bound connections[] + .platform.
MASHUP='section Section1;
shared Titanic = let
 Source = Csv.Document(Web.Contents("'"$URL"'"), [Delimiter=",", Encoding=65001, QuoteStyle=QuoteStyle.Csv]),
 Headers = Table.PromoteHeaders(Source, [PromoteAllScalars=true])
in Headers;'

COMPOSITE_ID="{\"ClusterId\":\"$CLUSTER_ID\",\"DatasourceId\":\"$CONN_ID\"}"
QUERY_META=$(jq -n --arg name "$DF_NAME" --arg cid "$COMPOSITE_ID" --arg url "$URL" --arg qid "$(uuidgen)" '{
 formatVersion: "202502",
 name: $name,
 queriesMetadata: { Titanic: { queryId: $qid, queryName: "Titanic" } },
 connections: [ { connectionId: $cid, kind: "Web", path: $url } ]
}')
PLATFORM=$(jq -n --arg name "$DF_NAME" --arg lid "$(uuidgen)" '{
 "$schema": "https://developer.microsoft.com/json-schemas/fabric/gitIntegration/platformProperties/2.0.0/schema.json",
 metadata: { type: "Dataflow", displayName: $name },
 config: { version: "2.0", logicalId: $lid }
}')

MASHUP_B64=$(echo -n "$MASHUP" | base64 -w0)
META_B64=$(echo -n "$QUERY_META" | base64 -w0)
PLAT_B64=$(echo -n "$PLATFORM" | base64 -w0)

BODY_FILE=$(mktemp --suffix=.json 2>/dev/null || mktemp) # GNU + BSD/macOS compatible
cat > "$BODY_FILE" &2; exit 1; }

# Step 7 (optional): Validate the M evaluates — top-level QueryName, PascalCase.
EQ_BODY=$(mktemp --suffix=.json 2>/dev/null || mktemp)
printf '{"QueryName":"Titanic"}' > "$EQ_BODY"
az rest --method post --resource "$RES" \
 --url "$API/workspaces/$WS_ID/dataflows/$DF_ID/executeQuery" \
 --headers "Content-Type=application/json" \
 --body "@$EQ_BODY" --output-file /tmp/titanic.arrow
rm -f "$EQ_BODY"
# Apache Arrow stream — embedded {"Error":"..."} means failure even on HTTP 200.
grep -q '"Error":"' /tmp/titanic.arrow && { echo "executeQuery surfaced an error." >&2; exit 1; }

# Step 8 (optional): Trigger refresh with ApplyChangesIfNeeded on first run — see Example 2.

Example 2: Trigger a Refresh Job

Prompt: "Trigger a refresh on this dataflow and poll until it completes."

Agent response:

Copy & paste — that's it
# Trigger refresh (returns 202 + Location header for polling).
# jobType MUST be "Refresh"; "Pipeline" returns 400 InvalidJobType.
# On the first refresh after any updateDefinition, body MUST include executeOption=ApplyChangesIfNeeded
# (otherwise Fabric refreshes the previously-applied definition).
# Acquire $TOKEN per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = https://api.fabric.microsoft.com).
LOCATION=$(curl -sS -X POST \
 -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
 --data '{"executionData":{"executeOption":"ApplyChangesIfNeeded"}}' \
 "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/dataflows/${DF_ID}/jobs/instances?jobType=Refresh" \
 -o /dev/null -D - | tr -d '\r' | grep -i "^location:" | awk '{print $2}')

# Poll until terminal (Fabric refresh job status enum: NotStarted / InProgress / Completed / Failed / Cancelled).
while true; do
 STATUS=$(az rest --method get --url "$LOCATION" \
 --resource "https://api.fabric.microsoft.com" --query "status" -o tsv)
 echo "Status: $STATUS"
 [[ "$STATUS" == "Completed" || "$STATUS" == "Failed" || "$STATUS" == "Cancelled" ]] && break
 sleep 10
done

PowerShell variant (Invoke-WebRequest exposes response headers natively; avoids the tr | grep | awk pipe):

Copy & paste — that's it
# Notes:
# - $Resp.Headers["Location"] returns string or string[] depending on PS version — never
# use .Location[0] (returns first character on Windows PS 5.1 plain-string case).
# - Wrap Invoke-WebRequest in try/catch on 5.1 (-SkipHttpErrorCheck is PS 7+).
# - Fabric refresh job status enum: NotStarted / InProgress / Completed / Failed / Cancelled.
# This is distinct from the LRO operation enum (Running / Succeeded / Failed / Cancelled).
# Refresh "success" = "Completed", not "Succeeded".
# Acquire $Token per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = https://api.fabric.microsoft.com).
try {
 $Resp = Invoke-WebRequest -Method POST -UseBasicParsing `
 -Uri "https://api.fabric.microsoft.com/v1/workspaces/$WS_ID/dataflows/$DF_ID/jobs/instances?jobType=Refresh" `
 -Headers @{ Authorization = "Bearer $Token"; "Content-Type" = "application/json" } `
 -Body '{"executionData":{"executeOption":"ApplyChangesIfNeeded"}}'
} catch {
 Write-Error "Refresh trigger failed: $($_.Exception.Message)"; exit 1
}
$Location = $Resp.Headers["Location"]
if ($Location -is [array]) { $Location = $Location[0] }

while ($true) {
 $Status = az rest --method get --url $Location `
 --resource "https://api.fabric.microsoft.com" --query "status" -o tsv
 Write-Host "Status: $Status"
 if ($Status -in 'Completed','Failed','Cancelled') { break }
 Start-Sleep -Seconds 10
}

Example 3: Modify an Existing Dataflow's Definition

Prompt: "Update the mashup of an existing dataflow with a modified query."

Agent response — read-modify-write loop. getDefinition returns sync 200 in the typical case; this template handles the 202 + LRO branch as well.

Copy & paste — that's it
RESOURCE="https://api.fabric.microsoft.com"
# Acquire $TOKEN per common/COMMON-CLI.md § Token-in-Variable Pattern (resource = $RESOURCE).

# 1. Read current definition (sync 200 or 202 LRO — handle both).
HDR=$(mktemp); BODY=$(mktemp)
CODE=$(curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Length: 0" \
 "$RESOURCE/v1/workspaces/${WS_ID}/dataflows/${DF_ID}/getDefinition" \
 -D "$HDR" -o "$BODY" -w "%{http_code}")
if [ "$CODE" = "202" ]; then
 LOC=$(tr -d '\r' &2; exit 1 ;;
 esac
 done
else
 DEF=$(cat "$BODY")
fi
rm -f "$HDR" "$BODY"

# 2. Decode each part, modify mashup.pq, re-encode all 3.
MASHUP=$(echo "$DEF" | jq -r '.definition.parts[] | select(.path=="mashup.pq") | .payload' | base64 -d)
META=$( echo "$DEF" | jq -r '.definition.parts[] | select(.path=="queryMetadata.json") | .payload' | base64 -d)
PLAT=$( echo "$DEF" | jq -r '.definition.parts[] | select(.path==".platform") | .payload' | base64 -d)

NEW_MASHUP=$(echo "$MASHUP" | sed 's/old-pattern/new-pattern/') # edit M here

MASHUP_B64=$(echo -n "$NEW_MASHUP" | base64 -w0)
META_B64=$(echo -n "$META" | base64 -w0)
PLAT_B64=$(echo -n "$PLAT" | base64 -w0)

# 3. Build the updateDefinition body in a temp file (full replacement — all 3 parts).
BODY_FILE=$(mktemp --suffix=.json 2>/dev/null || mktemp) # GNU + BSD/macOS compatible
cat > "$BODY_FILE" 
 Binding a new connection? Example 1 (steps 1-5) is the canonical bind+save flow. Bind-only walk-throughs live in [authoring-cli-quickref.md § Connection Binding Quick Patterns](https://github.com/microsoft/skills-for-fabric/blob/main/skills/dataflows-authoring-cli/references/authoring-cli-quickref.md#connection-binding-quick-patterns) and [authoring-script-templates.md § Connection Binding Templates](https://github.com/microsoft/skills-for-fabric/blob/main/skills/dataflows-authoring-cli/references/authoring-script-templates.md#connection-binding-templates).

## Output Expectations

When this skill completes a task, the agent should return:

 Field Convention 
 **Verbosity** Concise summary (3–10 lines) of what was created/modified. 
 **Default format** Markdown for status reports; fenced JSON code block for single-resource responses; markdown table for list responses. 
 **Side-effect disclosure** Explicitly report IDs created/modified/deleted and the target workspace ID. Never imply success without an ID. When you saved or replaced a dataflow definition, name the parts you wrote in prose — `mashup.pq`, `queryMetadata.json`, `.platform` — since long command bodies are truncated in the transcript and the part names would otherwise be lost. 
 **Verification** Re-`GET` the affected resource (dataflow, connection, job instance) and surface its state (e.g., `provisionState`, `status`, `Completed`) before declaring done. 
 **Error surfacing** If any step returned a non-2xx status, an LRO `Failed`/`Cancelled`, or an Arrow-stream `{"Error":"..."}`, propagate the raw error verbatim and stop. 
 **Preview rendering (Workflow C)** After `executeQuery`, render `head(10)` of the result as a markdown table in chat alongside the saved Arrow file — even when the embedded-error check passes. Catches silent-success bugs (filter dropped all rows, wrong column, off-by-one, wrong cast) that the embedded-error detector cannot see. Snippet + suppression rules: [dataflows-consumption-cli § Example 5b](https://github.com/microsoft/skills-for-fabric/blob/main/skills/dataflows-authoring-cli/../dataflows-consumption-cli/SKILL.md#example-5b-render-query-results-as-a-markdown-table). 
 **API names** When the answer references API endpoints or request-body fields, use their exact, case-sensitive names (`executeQuery`, `customMashupDocument`, `QueryName`, `mashup.pq`, `queryMetadata.json`, `GET /v1/connections/supportedConnectionTypes`) rather than paraphrased or pluralized variants.