npx skills add ...
npx skills add microsoft/skills-for-fabric --skill dataflows-save-as-authoring-cli
npx skills add microsoft/skills-for-fabric --skill dataflows-save-as-authoring-cli
Copy, clone, duplicate, rebind, and execute Gen1 dataflow save-as upgrade operations via CLI (az rest / curl) against Power BI REST and Fabric REST APIs. Covers Gen1 to Gen2.1 upgrade save-as, cross-workspace Gen1-to-Gen2.1 copy flows, connection rebinding, output changes, readiness snapshots, and SaveAsNativeArtifact execution. Use when the user wants to: (1) discover Gen1 dataflows, (2) assess save-as readiness, (3) upgrade Gen1 into Gen2.1, (4) create a Gen2.1 copy from a Gen1 dataflow in another workspace, (5) rebind connections or validate saved data. For creating/editing Gen2 dataflows from scratch, previewing candidate M, binding ordinary authoring connections, or copying Gen2 dataflows, use `dataflows-authoring-cli`. Triggers: "save Gen1 dataflow", "upgrade dataflow", "Gen1 Gen2 readiness", "Gen1 Gen2 save-as", "saveAsNativeArtifact", "copy Gen1 dataflow", "duplicate Gen1 dataflow", "clone Gen1 dataflow", "rebind Gen1 dataflow connections".
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-updatesskill.- 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
GENERATION BOUNDARY -- HARD STOP (mandatory) This skill executes save-as only from Gen1 to Gen2.1. For an execution request whose source is Gen2, or whose source generation is not established, state that no public save-as or in-place upgrade endpoint is available and stop before API calls. This does not block a read-only readiness scan whose purpose is to discover and classify Gen1 candidates. Do not interpret "choose the closest endpoint and proceed" as approval to export a definition and create a copy, do not invoke the authoring skill automatically, and do not mutate anything. Ask the user to clarify the intended outcome and explicitly approve any separately documented alternative.
A save-as companion for creating upgraded Gen2.1 copies from Power BI Gen1 dataflows using readiness assessment and guarded execution.
We currently cannot perform an in-place migration of your dataflow. We can use save-as to create an upgraded Gen2.1 copy while preserving the original Gen1 dataflow.
| Task | Reference | Notes |
|---|---|---|
| Finding Workspaces and Items in Fabric | COMMON-CLI.md § Finding Workspaces and Items | Mandatory — READ link first |
| Fabric Topology & Key Concepts | COMMON-CORE.md § Fabric Topology | |
| Environment URLs | COMMON-CORE.md § Environment URLs | |
| Authentication & Token Acquisition | COMMON-CORE.md § Authentication | Wrong audience = 401 |
| Core Control-Plane REST APIs | COMMON-CORE.md § Core REST APIs | Pagination, LRO polling, rate limits |
| Tool Selection Rationale | COMMON-CLI.md § Tool Selection | |
| Authentication Recipes | COMMON-CLI.md § Auth Recipes | az login flows and token acquisition |
Fabric Control-Plane API via az rest | COMMON-CLI.md § az rest | Always pass --resource |
| Gotchas & Troubleshooting (CLI) | COMMON-CLI.md § Gotchas | az rest audience, shell escaping |
| Quick Reference | COMMON-CLI.md § Quick Ref | Token audience / tool matrix |
| Dataflow Definition Structure | DATAFLOWS-AUTHORING-CORE.md § Definition | 3-part format for Gen2 CI/CD |
| Consumption Capability Matrix | DATAFLOWS-CONSUMPTION-CORE.md § Capabilities | Read-only discovery patterns |
| Upgrade CLI Quick Reference | upgrade-cli-quickref.md | All az rest one-liners for scanning & save-as |
| Risk Assessment Guide | risk-assessment-guide.md | Risk signal detection logic & API calls |
| Tool | Role | Install |
|---|---|---|
az CLI | Primary: Auth (az login), REST API calls (az rest) against both Fabric and Power BI APIs. | Pre-installed in most dev environments |
jq | Parse and filter JSON responses (dataflow lists, risk signal extraction). | Pre-installed or trivial |
base64 | Decode dataflow definitions for inspection. | Built into bash / [Convert]::ToBase64String() in PowerShell |
Agent check — verify before first operation:
This skill uses two distinct API audiences. Using the wrong audience returns 401.
| API | Audience (--resource) | Use For |
|---|---|---|
| Fabric Items API | https://api.fabric.microsoft.com | List Gen2 dataflows (Fabric-native), workspace discovery |
| Power BI REST API | https://analysis.windows.net/powerbi/api | Gen1 dataflow discovery, saveAsNativeArtifact, data sources, upstream dataflows, Admin API scanning |
Goal: "Should I use save-as, and what will happen when I create a Gen2.1 copy?"
Follow this sequence for every save-as assessment:
The Power BI REST API returns a generation property (value 1 or 2) on each dataflow. This is the preferred detection method — a single API call per workspace.
Tip: A
modelUrlpointing todfs.core.windows.netadditionally indicates BYOSA (customer-managed storage) — a save-as blocker.
Requires Fabric administrator role or service principal with Tenant.Read.All scope. Rate limited to 200 requests/hour.
Note: The Admin API supports
$filter,$top, and$skipfor pagination on large tenants.
For each Gen1 dataflow found, evaluate seven risk signals. See risk-assessment-guide.md for detailed API calls.
| # | Risk Signal | Detection Method | Impact |
|---|---|---|---|
| 1 | Incremental refresh | Check dataflow definition for incremental refresh policy configuration | ⚠️ Schedule migrates in disabled state; must re-enable and validate |
| 2 | BYOSA / Custom ADLS Gen2 storage | Check modelUrl — if points to customer storage account (not Power BI managed) | ❌ Data stays in old storage; Gen2 CI/CD uses Fabric-managed storage |
| 3 | Power Automate / API triggers | Check for external orchestration referencing the Gen1 dataflow ID | ⚠️ All integrations must update to new Gen2 artifact ID |
| 4 | Downstream pipeline dependencies | Check Fabric pipelines for dataflow activity references | ⚠️ Pipeline activities reference dataflow by ID; must re-bind |
| 5 | Linked / computed entities | Inspect dataflow definition for entity references to other dataflows | ⚠️/❌ Cross-dataflow references may break if source dataflows are not saved first |
| 6 | DirectQuery connections | Inspect data source types in definition | ❌ DirectQuery not supported in Gen2 CI/CD dataflows |
| 7 | Caller is not owner / insufficient role | Compare configuredBy against az account show --query user.name -o tsv — or attempt call and catch DataflowUnauthorizedError | ❌ saveAsNativeArtifact requires the caller to be the dataflow owner or have Contributor/Admin in the source workspace; Viewer/Member without ownership cannot execute save-as |
| Category | Criteria | Action |
|---|---|---|
| ✅ Safe | No risk signals detected | Create a Gen2.1 save-as copy with saveAsNativeArtifact |
| ⚠️ Manual followups | Risk signals 1, 3, 4, or 5 (non-blocking) | Execute save-as, then remediate flagged issues |
| ❌ Blocked | Risk signals 2, 6, or 7 (blocking) | Cannot execute save-as until blocker is resolved |
Tip — detect ownership before save-as: The
configuredByfield in the dataflow list response contains the owner's email. Compare it against the currently logged-in user (az account show --query user.name -o tsv). If they don't match and your workspace role is below Contributor, flag the dataflow as ❌ Blocked (signal 7) and escalate to the owner.
Save JSON to file: pipe to
jq '.' > readiness-snapshot.json
Goal: Invoke save-as and capture outcomes safely.
saveAsNativeArtifact APIThis is a Preview API. It creates a new Gen2.1 CI/CD artifact copy while preserving the original Gen1 dataflow.
Gotcha — inline body: Passing JSON inline via
--body '{...}'can causeaz restto wrap the payload in an extra envelope, resulting in"saveAsRequest is a required parameter"errors. Always use file-based body (--body @file.json) for this endpoint.
Gotcha — Windows
az.cmd: On Windows, omit-o jsonfromsaveAsNativeArtifactcalls — the flag produces"A value that is not valid (json) was specified for the outputFormat parameter"when routed throughaz.cmd. Capture output without-o jsonand parse withConvertFrom-Jsonin PowerShell, or pipe tojqin bash.
Gotcha — not idempotent (duplicate artifacts on retry):
saveAsNativeArtifactcreates a new artifact every time it is called. If a batch is interrupted and re-run, you will end up with multiple copies in the target workspace. To make retries safe: (1) check whether a Gen2 artifact with the intended name already exists before calling, or (2) include a timestamp indisplayNameand treat each run as a distinct artifact.
Gotcha — owner permissions: You must be the dataflow owner or have Contributor/Admin in the source workspace to call
saveAsNativeArtifact. If you are only a Viewer or Workspace Member who does not own the dataflow, the API returnsDataflowUnauthorizedError. Ask the dataflow owner or a workspace admin to run the save-as operation for those dataflows.
Request parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
displayName | string (max 200) | No | Name for new artifact. Auto-generated with _copy1 suffix if omitted |
description | string (max 4000) | No | Description. Copied from source if omitted |
includeSchedule | boolean | No | Copy refresh schedule in disabled state |
targetWorkspaceId | string (uuid) | No | Target workspace. Same workspace if omitted |
Response: 200 OK with SaveAsNativeDataflowResponse:
artifactMetadata — full metadata of the new Gen2 CI/CD artifact (including objectId, provisionState)errors[] — non-fatal warning codes (save-as succeeds even if these occur):
FailedToCopySchedule — schedule could not be copiedSetDataflowOriginFailed — origin tracking not setConnectionsUpdateFailed — connection strings could not be updated to Fabric formatNOT YET AVAILABLE — This API is not available in the current public surface. This skill will be updated when the endpoint is published. Do not attempt to call a non-existent endpoint.
Run these checks after save-as before any Gen1 cleanup:
artifactMetadata.provisionState reaches Active.errors[] in SaveAsNativeDataflowResponse and create follow-up tasks for each warning.--resource to az rest — use the correct audience per the API table above. Wrong audience = silent 401.--headers "Content-Type=application/json" on POST calls to the Power BI REST API.saveAsNativeArtifact — pass --body @file.json instead of inline JSON. Inline --body '{...}' can cause az rest to wrap the payload in an extra envelope, producing "saveAsRequest is a required parameter" errors.-o json on saveAsNativeArtifact calls — use ConvertFrom-Json in PowerShell or pipe to jq instead. The -o json flag fails with "A value that is not valid (json)" error when routed through az.cmd.saveAsNativeArtifact returns DataflowUnauthorizedError for non-owners who are only Workspace Members or Viewers.saveAsNativeArtifact is not idempotent; interrupted batch runs create duplicate copies on retry. Either verify the target name is absent before calling, or use a unique timestamped displayName per run.errors[] in saveAsNativeArtifact response — save-as may succeed with non-fatal warnings.provisionState is Active after save-as — poll the artifact metadata until terminal state.saveAsNativeArtifact leaves the Gen1 intact. Do not delete it until post-save-as validation passes.generation property for Gen1 detection — the Power BI REST API returns generation: 1 or generation: 2 on each dataflow. Use this single-API-call approach over cross-API comparison.modelUrl check as secondary signal — dataflows with a modelUrl pointing to dfs.core.windows.net are Gen1 using customer storage (BYOSA blocker).FailedToCopySchedule, ConnectionsUpdateFailed, etc. require manual follow-up.-o json with saveAsNativeArtifact on Windows — omit the flag; capture raw output and parse it explicitly.Prompt: "Scan my Sales workspace for Gen1 dataflows and show me a readiness snapshot"
Agent response:
Prompt: "Migrate or upgrade the SalesETL Gen1 dataflow in my Sales workspace"
Agent response:
Prompt: "Show me all Gen1 dataflows across the entire tenant"
Agent response: