| name | dataflows-authoring-cli |
| description | Create, update, delete, and manage Fabric Dataflows Gen2 artifacts with Power Query M mashup definitions via CLI (az rest / curl). Uses az rest and curl against the Fabric REST API to author definitions containing base64-encoded mashup.pq, queryMetadata.json, and .platform parts. Supports creating dataflows with inline definitions, modifying mashup queries, binding connections, triggering Execute refresh jobs with typed parameter overrides, and exporting definitions for CI/CD. Use when the user wants to: (1) create a new Dataflow Gen2 with Power Query M queries, (2) update a dataflow mashup definition, (3) trigger a dataflow refresh job, (4) bind or manage dataflow connections, (5) set up CI/CD via definition export and import, (6) delete a dataflow, (7) configure staging destinations. Triggers: "create dataflow", "author dataflow", "Power Query M", "mashup document", "update dataflow definition", "refresh dataflow", "dataflow connection", "ETL dataflow", "dataflow CI/CD".
|
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 Gen2 — Authoring via CLI
Table of Contents
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 |
Agent check — verify before first operation:
az --version 2>/dev/null || echo "INSTALL: https://aka.ms/install-azure-cli"
jq --version 2>/dev/null || echo "INSTALL: apt-get install jq OR brew install jq"
Connection
Discover Workspace and Dataflow IDs
Per COMMON-CLI.md Finding Workspaces and Items in Fabric:
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
WS_ID="<workspaceId>"
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
WS_ID="<workspaceId>"
DF_ID="<dataflowId>"
API="https://api.fabric.microsoft.com/v1"
RESOURCE="https://api.fabric.microsoft.com"
Agentic Workflows
Discover → Formulate → Execute → Verify
- Discover → List workspaces, list dataflows, get definition (decode to inspect M code and metadata).
- Formulate → Create or modify M code (
mashup.pq), update queriesMetadata, prepare all 3 definition parts.
- Execute → Create or update dataflow via REST API, optionally trigger refresh.
- Verify → Poll LRO for completion, get definition again to confirm changes, check refresh status.
RESULT=$(az rest --method post \
--resource "$RESOURCE" \
--url "$API/workspaces/$WS_ID/dataflows/$DF_ID/getDefinition")
echo "$RESULT" | jq -r '.definition.parts[] | select(.path=="mashup.pq") | .payload' | base64 -d
az rest --method post \
--resource "$RESOURCE" \
--url "$API/workspaces/$WS_ID/dataflows/$DF_ID/updateDefinition?updateMetadata=true" \
--body @definition.json
LOCATION=$(az rest --method post \
--resource "$RESOURCE" \
--url "$API/workspaces/$WS_ID/items/$DF_ID/jobs/Execute/instances" \
--headers "Content-Length=0" \
--output none --include-response-headers 2>&1 | grep -i "^location:" | awk '{print $2}' | tr -d '\r')
Gotchas, Rules, Troubleshooting
For full authoring gotchas: DATAFLOWS-AUTHORING-CORE.md Gotchas and Troubleshooting.
For CLI-specific issues: COMMON-CLI.md Gotchas & Troubleshooting (CLI-Specific).
MUST DO
az login first — all az rest calls use the active session. No session → 401.
- Always
--resource "https://api.fabric.microsoft.com" — wrong audience = 401.
- Base64-encode all definition parts — REST API expects
payloadType: "InlineBase64".
- Send all 3 parts in updateDefinition — it's a full replacement, not incremental.
- Handle LRO polling —
getDefinition, updateDefinition (with def), and job execution return 202; poll via Location header.
- Verify workspace has capacity —
GET /v1/workspaces/{id} and check capacityId.
- Set
formatVersion to "202502" in queryMetadata.json.
- Set
loadEnabled: true for queries that should write output on refresh.
AVOID
- Hardcoded GUIDs — discover workspace/dataflow IDs via REST API (Connection section).
- Skipping base64 encoding — payloads will be rejected or corrupted.
- Partial definition updates — sending 1 or 2 parts silently drops queries.
- Creating dataflows with unbound connections — queries referencing nonexistent
connectionId will fail at refresh.
- Using GET for
getDefinition — it's a POST endpoint; GET returns 405.
- Constructing operation URLs manually — always use the
Location header from 202 responses.
- Duplicate
displayName values — not enforced but causes confusion.
PREFER
az rest over raw curl — handles token acquisition and refresh automatically.
getDefinition before updateDefinition — read-modify-write prevents accidental data loss.
- Validate M code before creating dataflow — syntax errors won't surface until refresh.
jq for JSON manipulation — build definition payloads programmatically.
"Automatic" for parameter type in job execution — lets engine infer from definition.
?updateMetadata=true on updateDefinition — ensures .platform changes (display name) are applied.
- Env vars (
WS_ID, DF_ID, API, RESOURCE) for script reuse.
TROUBLESHOOTING
| Symptom | Fix |
|---|
| 401 Unauthorized | Verify az login is active; check --resource "https://api.fabric.microsoft.com" |
| 405 Method Not Allowed on getDefinition | Use POST, not GET — getDefinition follows LRO pattern |
| updateDefinition silently drops queries | Send all 3 parts (queryMetadata.json, mashup.pq, .platform) |
| Refresh fails on new dataflow | Ensure connectionId values exist in user's Fabric connection store |
| formatVersion mismatch error | Set formatVersion to "202502" in queryMetadata.json |
| Query doesn't produce output | Set loadEnabled: true in queriesMetadata |
| Fast copy not engaged | Add [StagingDefinition = [Kind = "FastCopy"]] before section in mashup.pq |
| LRO polling returns 404 | Use Location header URL — don't construct operation URLs manually |
| 429 Too Many Requests | Respect Retry-After header; implement exponential backoff |
| base64 decode produces garbage | Ensure no trailing newlines; use base64 -w0 (Linux) for encoding |
Examples
Example 1: Create a Dataflow Gen2 with Inline Definition
MASHUP='section Section1;
shared Output = let
Source = #table(type table [Name = text, Value = number],
{{"Alpha", 100}, {"Beta", 200}, {"Gamma", 300}})
in Source;'
QUERY_META='{
"formatVersion": "202502",
"queriesMetadata": {
"Output": { "queryId": "00000000-0000-0000-0000-000000000001",
"queryName": "Output", "loadEnabled": true }
}
}'
PLATFORM='{"$schema":"https://developer.microsoft.com/json-schemas/fabric/gitIntegration/platformProperties/2.0.0/schema.json","metadata":{"type":"Dataflow","displayName":"MyDataflow"},"config":{"version":"2.0","logicalId":"00000000-0000-0000-0000-000000000001"}}'
MASHUP_B64=$(echo -n "$MASHUP" | base64 -w0)
META_B64=$(echo -n "$QUERY_META" | base64 -w0)
PLAT_B64=$(echo -n "$PLATFORM" | base64 -w0)
az rest --method post \
--url "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/items" \
--resource "https://api.fabric.microsoft.com" \
--headers "Content-Type=application/json" \
--body "{
\"displayName\": \"MyDataflow\",
\"type\": \"Dataflow\",
\"definition\": {
\"parts\": [
{\"path\": \"mashup.pq\", \"payload\": \"${MASHUP_B64}\", \"payloadType\": \"InlineBase64\"},
{\"path\": \"queryMetadata.json\", \"payload\": \"${META_B64}\", \"payloadType\": \"InlineBase64\"},
{\"path\": \".platform\", \"payload\": \"${PLAT_B64}\", \"payloadType\": \"InlineBase64\"}
]
}
}"
Example 2: Trigger a Refresh Job
LOCATION=$(az rest --method post \
--url "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/items/${DF_ID}/jobs/instances?jobType=Pipeline" \
--resource "https://api.fabric.microsoft.com" \
--headers "Content-Length=0" \
--output none --include-response-headers 2>&1 | grep -i "^location:" | awk '{print $2}' | tr -d '\r')
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" ]] && break
sleep 10
done
Example 3: Update a Dataflow Definition
LOCATION=$(az rest --method post \
--url "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/items/${DF_ID}/getDefinition" \
--resource "https://api.fabric.microsoft.com" \
--headers "Content-Length=0" \
--output none --include-response-headers 2>&1 | grep -i "^location:" | awk '{print $2}' | tr -d '\r')
DEF=$(az rest --method get --url "${LOCATION}" \
--resource "https://api.fabric.microsoft.com")
az rest --method post \
--url "https://api.fabric.microsoft.com/v1/workspaces/${WS_ID}/items/${DF_ID}/updateDefinition" \
--resource "https://api.fabric.microsoft.com" \
--headers "Content-Type=application/json" \
--body "{\"definition\": {\"parts\": [ ... ]}}"